1

我有一个带有反应路由器 v4、webpack 4 的标准 React-Redux 应用程序,我正在尝试通过可加载组件库执行组件的延迟加载,因此 webpack 可以创建块并按需加载它们。问题似乎总是在Dashboard内部渲染(参考下面的代码)组件<Switch>,无论路线如何。

我不明白原因。我发现了一个类似的问题:React-Router v4 渲染错误的组件但正确匹配,但它遵循不同的模式,我不能或不明白如何将该解决方案应用于我的问题。

我正在发布主 AppContainer 和路由文件:

AppContainer.js:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import ReduxToastr from 'react-redux-toastr'
import 'react-redux-toastr/src/styles/index.scss'
import { Offline, Online } from 'react-detect-offline'
import Footer from 'components/Footer/'
import { Modal, ModalHeader, ModalBody } from 'reactstrap'
import { Translate } from 'react-redux-i18n'
import { PersistGate } from 'redux-persist/integration/react'
import { Router, Switch, Route } from 'react-router-dom'
import PrivateRoute from 'components/PrivateRoute'
import { Dashboard, PasswordReset, PasswordResetEdit, SignIn } from 'routes'

// Layout
import CoreLayout from 'layouts/CoreLayout'

class AppContainer extends Component {
  static propTypes = {
    history: PropTypes.object.isRequired,
    persistor: PropTypes.object.isRequired, // redux-persist
    store  : PropTypes.object.isRequired
  }

  render () {
    const { history, store, persistor } = this.props
    const opened = true
    const toastrDefaultTimeout = 8000
    const newToastrAlwaysOnTop = false

    return (
      <div>
        <Online>
          <Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
              <div style={{ height: '100%' }}>
                <ReduxToastr
                  timeOut={toastrDefaultTimeout}
                  newestOnTop={newToastrAlwaysOnTop}
                  preventDuplicates
                  position='top-right'
                  transitionIn='fadeIn'
                  transitionOut='fadeOut'
                  progressBar />
                <Router history={history}>
                  <CoreLayout {...this.props} >
                    <Switch>
                      <Route exact path='/sign-in' component={SignIn} />
                      <Route exact path='/passwords/new' component={PasswordReset} />
                      <Route exact path='/passwords/edit' component={PasswordResetEdit} />
                      <PrivateRoute exact path='/' component={Dashboard} persistor={persistor} />
                    </Switch>
                  </CoreLayout>
                </Router>
              </div>
            </PersistGate>
          </Provider>
        </Online>
        <Offline>
          <div className='app'>
            <div className='app-body'>
              <main className='main align-items-center align-self-center'>
                <div className='container'>
                  <div className='animated fadeIn'>
                    <Modal isOpen={opened} toggle={this.toggle}>
                      <ModalHeader toggle={this.toggle}><Translate value={`views.shared.no-internet.title`} /></ModalHeader>
                      <ModalBody>
                        <div className='card-block'>
                          <div className='row'>
                            <div className='col-sm-12'>
                              <Translate value='views.shared.no-internet.description' />
                            </div>
                          </div>
                        </div>
                      </ModalBody>
                    </Modal>
                  </div>
                </div>
              </main>
            </div>
            <Footer />
          </div>
        </Offline>
      </div>
    )
  }
}

export default AppContainer

路线/index.js:

// We only need to import the modules necessary for initial render
import loadable from 'loadable-components'
import { hot } from 'react-hot-loader'

// Component split-code (lazy load)
let Dashboard,
SignIn,
PasswordReset,
PasswordResetEdit

// Needed for HMR
if (__DEV__) {
  Dashboard = hot(module)(loadable(() => import('./Dashboard')))
  PasswordReset = hot(module)(loadable(() => import('./PasswordReset')))
  PasswordResetEdit = hot(module)(loadable(() => import('./PasswordResetEdit')))
  SignIn = hot(module)(loadable(() => import('./SignIn')))
} else {
  Dashboard = loadable(() => import('./Dashboard'))
  SignIn = loadable(() => import('./SignIn'))
  PasswordReset = loadable(() => import('./PasswordReset'))
  PasswordResetEdit = loadable(() => import('./PasswordResetEdit'))
}

export { Dashboard, PasswordReset, PasswordResetEdit, SignIn }

对于任何好奇的人,这里是PrivateRoute组件:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Route, Redirect } from 'react-router-dom'
import { connect } from 'react-redux'
import { validateSession } from 'modules/sessions/session'

const mapStateToProps = state => ({
  session: state.session
})

const mapDispatchToProps = (dispatch) => {
  return {
    validateSession: () => { dispatch(validateSession()) }
  }
}

class PrivateRoute extends Component {
  componentDidMount () {
    this.props.validateSession(this.props.persistor)
  }

  render () {
    const { component: Component, session, ...rest } = this.props

    return (
      <Route {...rest} render={(routeProps) =>
      this.props.session.userSignedIn ? <Component {...routeProps} />
      : <Redirect to={{
        pathname: '/sign-in',
        state: { from: routeProps.location }
      }} />
    }
  />)
  }
}

PrivateRoute.propTypes = {
  component: PropTypes.func.isRequired,
  persistor: PropTypes.object.isRequired,
  session: PropTypes.object.isRequired,
  validateSession: PropTypes.func.isRequired
}

export default connect(mapStateToProps, mapDispatchToProps)(PrivateRoute)

如果我删除了if (_DEV_)routes/index.js 中的块,并且总是像“else”块一样做,一切正常,但是我失去了 HMR,这意味着我必须刷新浏览器才能看到代码更改生效。

编辑

对于任何徘徊历史道具来源的人:主应用程序入口点(在 webpack.config.js 中)。src/main.js:

import React from 'react'
import ReactDOM from 'react-dom'
import createStore from './store/createStore'
import { hot } from 'react-hot-loader'
import AppContainer from './containers/AppContainer'
import { persistStore } from 'redux-persist'

// ========================================================
// Store Instantiation
// ========================================================

const initialState = window.___INITIAL_STATE__
const { history, store } = createStore(initialState)

// begin periodically persisting the store
let persistor = persistStore(store)

// ========================================================
// Render Setup
// ========================================================
const MOUNT_NODE = document.getElementById('root')

let render = () => {
  ReactDOM.render(
    <AppContainer store={store} persistor={persistor} history={history} />,
    MOUNT_NODE
  )
}

// This code is excluded from production bundle
if (__DEV__) {
  if (module.hot) {
    // Development render functions
    const renderApp = render
    const renderError = (error) => {
      const RedBox = require('redbox-react').default

      ReactDOM.render(<RedBox error={error} />, MOUNT_NODE)
    }

    // Wrap render in try/catch
    render = () => {
      try {
        renderApp()
      } catch (error) {
        console.error(error)
        renderError(error)
      }
    }

    // Setup hot module replacement
    module.hot.accept('./containers/AppContainer', () =>
      render(require('./containers/AppContainer').default)
    )
  }
}

// ========================================================
// Go!
// ========================================================
export default hot(module)(render)
render()

createStore.js:

import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk'
import { apiMiddleware } from 'redux-api-middleware'
import { createLogger } from 'redux-logger'
import makeRootReducer from './reducers'
import { routerMiddleware } from 'react-router-redux'
// I18n
import { syncTranslationWithStore, loadTranslations, setLocale } from 'react-redux-i18n'
import { translationsObject } from 'translations/index'
// Router history
import createHistory from 'history/createBrowserHistory'
// Raven for Sentry
import Raven from 'raven-js'
import createRavenMiddleware from 'raven-for-redux'

export default (initialState = {}) => {
  // ======================================================
  // Middleware Configuration
  // ======================================================
  const logger = createLogger()
  const history = createHistory()
  const historyMiddleware = routerMiddleware(history)

  const middleware = [historyMiddleware, apiMiddleware, thunk, logger]
  if (__PROD__) {
    Raven.config(`${__SENTRY_DSN__}`).install()
    middleware.push(createRavenMiddleware(Raven))
  }

  // ======================================================
  // Store Enhancers
  // ======================================================
  const enhancers = []

  let composeEnhancers = compose

  const composeWithDevToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  if (typeof composeWithDevToolsExtension === 'function') {
    composeEnhancers = composeWithDevToolsExtension
  }

  // ======================================================
  // Store Instantiation and HMR Setup
  // ======================================================
  const store = createStore(
    makeRootReducer(),
    initialState,
    composeEnhancers(
      applyMiddleware(...middleware),
      ...enhancers
    )
  )

  store.asyncReducers = {}

  // DEPRECATED in react-router v4: To unsubscribe, invoke `store.unsubscribeHistory()` anytime
  // store.unsubscribeHistory = browserHistory.listen(updateLocation(store))

  if (module.hot) {
    const reducers = require('./reducers').default
    module.hot.accept('./reducers', () => {
      store.replaceReducer(reducers(store.asyncReducers))
    })
  }

  syncTranslationWithStore(store)
  store.dispatch(loadTranslations(translationsObject))
  store.dispatch(setLocale('es_AR'))

  return { history, store }
}
4

0 回答 0