react-router-dom 路由组件需要页面重新加载

Posted

技术标签:

【中文标题】react-router-dom 路由组件需要页面重新加载【英文标题】:react-router-dom route component requires page reload 【发布时间】:2018-08-31 06:13:39 【问题描述】:

当调用 history.push('/packages') 时,url 会更新,但除非重新加载页面,否则组件不会挂载(渲染)。如果我调用 createHistory(forceRefresh: true) 或手动重新加载页面,则 UI 会正确呈现。如何配置 react-router-dom 以加载组件而不显式重新加载页面或使用 forceRefresh?

index.jsx

import React from 'react'
import ReactDOM from 'react-dom'
import  BrowserRouter  from 'react-router-dom'
import store from './store'
import Provider from 'react-redux'
import App from './App'

ReactDOM.render(
  <Provider store=store>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById('app')
);

App.jsx

import React,  Component  from 'react'
import  Switch, Route  from 'react-router-dom'
import PropTypes from 'prop-types'
import  Navbar, PageHeader, Grid, Row, Col  from 'react-bootstrap'
import LoginFormContainer from './components/Login/LoginFormContainer'
import PackageContainer from './components/Package/PackageContainer'

class App extends Component 
  render() 
    return (
      <Grid>
        <Navbar>
          <Navbar.Header>
            <Navbar.Brand><h3>Mythos</h3></Navbar.Brand>
          </Navbar.Header>
        </Navbar>
        <Row className="content">
          <Col xs=12 md=12>
            <Switch>
              <Route path='/packages' render=() => <PackageContainer /> />
              <Route exact path='/' render=() => <LoginFormContainer /> />
            </Switch>
          </Col>
        </Row>
      </Grid>
    )
  


export default App

loginActions.jsx

import * as types from './actionTypes'
import LoginApi from '../api/loginApi'
import createHistory from 'history/createBrowserHistory'

const history = createHistory()

export function loginUser(user) 
  return function(dispatch) 
    return LoginApi.login(user).then(creds => 
      dispatch(loginUserSuccess(creds));
    ).catch(error => 
      throw(error);
    );
  


export function loginUserSuccess(creds) 
  sessionStorage.setItem('credentials', JSON.stringify(creds.data))
  history.push('/packages')
  return 
    type: types.LOGIN_USER_SUCCESS,
    state: creds.data
  

PackageContainer.jsx

import React,  Component  from 'react'
import connect from 'react-redux'
import PropTypes from 'prop-types'
import loadPackages from '../../actions/packageActions'
import PackageList from './PackageList'
import ImmutablePropTypes from 'react-immutable-proptypes'
import Map,fromJS,List from 'immutable'
import withRouter from 'react-router-dom'

class PackageContainer extends Component 
  constructor(props, context) 
    super(props, context);
  
  componentDidMount() 
    this.props.dispatch(loadPackages());
  
  render() 
    return (
      <div className="col-lg-12">
        this.props.results ?
        <PackageList results=this.props.results /> :
        <h3>No Packages Available</h3>
      </div>
    );
  


PackageContainer.propTypes = 
  results: ImmutablePropTypes.list.isRequired,
;

const mapStateToProps = (state, ownProps) => 
  return 
    results: !state.getIn(['packages','packages','results']) ? List() : state.getIn(['packages','packages','results'])
  ;


PackageContainer = withRouter(connect(mapStateToProps)(PackageContainer))

export default PackageContainer

【问题讨论】:

尝试&lt;Route path='/packages' render=PackageContainer /&gt;并从您的代码中删除withRouter 【参考方案1】:

我假设,您正在创建 history 对象的新实例,但 BrowserRouter 不知道其中发生的变化。

因此,您应该创建历史对象并将其导出到 index.jsx 并使用 Router 而不是 BrowserRouter 并作为 history 属性传递,这样您就可以在需要时导入它。

例如:

index.jsx

import  Router  from 'react-router-dom'
import createHistory from 'history/createBrowserHistory'
...

export const history = createHistory()

ReactDOM.render(
  <Provider store=store>
    <Router history=history>
      <App />
    </Router>
  </Provider>,
  document.getElementById('app')
);

然后,在loginActions 中导入history 并像以前一样使用.push 方法。

loginActions.jsx

import * as types from './actionTypes'
import LoginApi from '../api/loginApi'
import  history  from './index'

export function loginUser(user) 
  return function(dispatch) 
    return LoginApi.login(user).then(creds => 
      dispatch(loginUserSuccess(creds));
    ).catch(error => 
      throw(error);
    );
  


export function loginUserSuccess(creds) 
  sessionStorage.setItem('credentials', JSON.stringify(creds.data))
  history.push('/packages')
  return 
    type: types.LOGIN_USER_SUCCESS,
    state: creds.data
  

希望它会有所帮助。

【讨论】:

这行得通,谢谢!那么,我是否需要使用 Router 而不是 BrowserRouter,因为我使用的是 redux? @neridaj 不完全是。实际上,BrowserRouter 忽略了 history 属性,所以我们应该使用自定义历史并将其传递给 Router。在这种情况下,它没有与Redux 连接。【参考方案2】:

在 App.jsx 中

<Switch>
     <Route path='/packages' render=(props) => <PackageContainer ...props/> />
     <Route exact path='/' render=(props) => <LoginFormContainer ...props/> />
 </Switch>

现在 PackageContainerLoginFormContainer 都可以访问 history 对象

【讨论】:

以上是关于react-router-dom 路由组件需要页面重新加载的主要内容,如果未能解决你的问题,请参考以下文章

React-路由 react-router-dom

页面未呈现;在 react-router-dom 中使用受保护的路由将道具传递给孩子时

“react-router-dom”路由器组件不起作用

React中使用路由进行父子组件的通信

React-router-dom:嵌套路由不起作用,而是从根组件应用程序中路由

使用react-router-dom@6,如何完成路由守卫这样的功能——使用高阶组件的包裹。