react-router4改变了URL,route不匹配新的URL重新渲染组件

如题,我用react-router4做登录的路由,当我登陆成功后,改变store中存储的currentURL,然后Router中监听到store的变化,用this.setState()改变Router的state,用History改变了页面的URL,并试图引起页面组件路由的重新渲染,render确实被调用了,路由却没有渲染新对应URL的组件。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import Routes from './Routes.js';
import store from './Store.js';
import {Provider} from 'react-redux';
import './css/bootstrap.min.css';
import './css/navbar/chartist-custom.css';
import './css/navbar/main.css';
import './css/navbar/font-awesome.min.css';
import './css/navbar/style.css';
import {createBrowserHistory} from 'history'

const history = createBrowserHistory();

ReactDOM.render(<Provider store={store}>
  <Routes history={history} /></Provider>, document.getElementById('root'));
registerServiceWorker();

Routes.js

import React, {Component} from 'react';
import {
  Route,
  Switch,
  Link,
  BrowserRouter,
  Router,
  Redirect
} from 'react-router-dom';
import LoginPage from './views/pages/LoginPage.js';
import SuccessPage from './views/pages/SuccessPage.js';
import errorPage from './views/pages/errorPage.js';
import store from './Store.js';

class Routes extends Component {

  constructor(props) {
    super(props);
    this.URLChange = this.URLChange.bind(this);
    this.getOwnState = this.getOwnState.bind(this);

    this.state = this.getOwnState();
  }

  getOwnState() {
    return {
      currentURL: store.getState()["currentURL"]
    };
  }

  URLChange() {
    console.debug(this.getOwnState()["currentURL"]);
    this.props.history.push(this.getOwnState()["currentURL"]);

    //setState是异步的
    let currentURL = this.getOwnState()["currentURL"];
    this.setState(Object.assign({
      currentURL
    }, {currentURL}), () => {
      //回调方法
      console.debug("1:" + this.state.currentURL)
    })

  }

  componentDidMount() {
    store.subscribe(this.URLChange);
  }

  render() {
    alert("render:" + JSON.stringify(this.props.history.location.pathname));
    return (<BrowserRouter >
      <Switch>
        <Route path="/LoginPage" component={LoginPage}/>
        <Route path="/SuccessPage" component={SuccessPage}/>
        <Route path="/errorPage" component={errorPage}/>
        <Route path="/*" component={errorPage}/>
      </Switch>
    </BrowserRouter>);
  }
}

export default Routes;

图片描述

此时如果刷新页面,则会渲染相应的新组件:

图片描述

调试一整天,无果,望各位指点一二


更新:
我在检测生命周期,发现componentWillUpdate和render执行之后,componentDIdUpdate并不执行。
不知道是不是render出错。

  componentWillUpdate() {
    alert("componentWillUpdate");
  }
  componentDIdUpdate() {
    alert("componentDIdUpdate");
  }

而在LoginPage的render和SuccessPage分别插入alert,发现走的是LoginPage的渲染和alert:

LoginPage.js

//展示组件
function LoginPage({login}) {
  alert("LoginPage");
  return (<div>
    <Panel style={PanelStyle}>
      <Image src={require('../../img/logo.png')} style={ImageStyle}/>
      <div style={TitleStyle}>公安民意评测系统</div>
      <FormGroup>
        <FormControl type="text" placeholder="账号" onChange={accountChange}/>
      </FormGroup>
      <FormGroup>
        <FormControl type="password" placeholder="密码" onChange={passwordChange}/>
      </FormGroup>
      <br/>
      <Button className="btn-block" bsStyle="primary" onClick={login}>登录</Button>
    </Panel>
  </div>);
}

SuccessPage.js

//展示组件
function SuccessPage() {
  alert("SuccessPage");
  return (<div>SuccessPage
  </div>);
}
阅读 9.4k
3 个回答
  • Stack Overflow上我也查了很久,还是没有查到点子上
  • shouldComponentUpdate(nextProps, nextState)在这个方法中写this.context.router.history.push("/路径");会造成无法渲染
  • 已把最终代码放上来了
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import Routes from './Routes.js';
import store from './Store.js';
import {Provider} from 'react-redux';
import './css/bootstrap.min.css';
import './css/navbar/chartist-custom.css';
import './css/navbar/main.css';
import './css/navbar/font-awesome.min.css';
import './css/navbar/style.css';
import {BrowserRouter} from 'react-router-dom';


const App = () => {
  return (<Provider store={store}>
    <BrowserRouter><Routes/></BrowserRouter>
  </Provider>);
}

ReactDOM.render(<App/>, document.getElementById('root'));

registerServiceWorker();
import React, {Component} from 'react';
import {
  Route,
  Switch,
  Link,
  BrowserRouter,
  Router,
  Redirect,
  withRouter
} from 'react-router-dom';
import LoginPage from './views/pages/LoginPage.js';
import SuccessPage from './views/pages/SuccessPage.js';
import errorPage from './views/pages/errorPage.js';
import store from './Store.js';
import {Provider} from 'react-redux';
import PropTypes from 'prop-types'

class Routes extends Component {

  constructor(props, context) {
    super(props, context);

    this.URLChange = this.URLChange.bind(this);
    this.getOwnState = this.getOwnState.bind(this);

    this.state = this.getOwnState();
  }

  static contextTypes = {
    router: PropTypes.object
  }


  getOwnState() {
    return {
      currentURL: store.getState()["currentURL"]
    };
  }

  URLChange() {
    console.debug(this.getOwnState()["currentURL"]);

    //setState是异步的
    let currentURL = this.getOwnState()["currentURL"];
    this.setState(Object.assign({
      currentURL
    }, {currentURL}), () => {
      //回调方法
      console.debug("回调方法执行完成this.state.currentURL:" + this.state.currentURL)
      console.debug("旧的URL:" + this.context.router.history.location.pathname);
      console.debug("新的URL:" + this.getOwnState()["currentURL"]);
      //改变路由
      this.context.router.history.push(this.getOwnState()["currentURL"]);
    })

  }
  componentDidMount() {
    store.subscribe(this.URLChange);
  }

  render() {
    return (<div>
      <Link to="/LoginPage">LoginPage</Link>
      <br/>
      <Link to="/SuccessPage">SuccessPage</Link>
      <br/>
      <Link to="/errorPage">errorPage</Link>
      <br/>
      <Switch>
        <Route exact="exact" path="/" component={LoginPage}/>
        <Route exact="exact" path="/LoginPage" component={LoginPage}/>
        <Route exact="exact" path="/SuccessPage" component={SuccessPage}/>
        <Route exact="exact" path="/errorPage" component={errorPage}/>
        <Route exact="exact" path="/*" component={errorPage}/>
      </Switch>
    </div>);
  }
  componentWillUpdate() {}
  componentDIdUpdate() {}

}

export default withRouter(Routes);

在使用react-router-dom的过程中,相信很多人都遇到了这个问题,下面有两种解决方式

注意:关键点在于两种不同Router的使用方式

// 4.0推荐的使用方式,Router中已经内置了history
import { BrowserRouter as Router } from 'react-router-dom';

// 虽然引入的是4.0,但是依旧可以使用3.0的使用方式,只是需要手动给Router传入history对象
import { Router } from 'react-router-dom';

解决方式1:4.0官方推荐的方式,给需要使用history的组件用withRouter将history导入

login.js

import { withRouter } from 'react-router-dom';

const Login = ({history}) => {
    history.push('/user'); // 这里使用不会有问题
    return null;
};

export default withRouter(Login); 

app.js

import { BrowserRouter, Route, Link, Switch } from 'react-router-dom';
import Login from './login';

const Home = () => (
    <div>Home
        <Link to="login">login</Link>
    </div>
)

export default () => (
    <BrowserRouter>
        <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/home" component={Home} />
            <Route path="/login" component={Login} />
        </Switch>
    </BrowserRouter>
);

解决方式2:3.0的使用方式(这种使用方式有个好处,那就是history可以在任意地方调用,比如你可以在axios拦截器中引入history,在token过期的时候完成前端重定向)

history.js

import createHistory from 'history/createBrowserHistory';

export default createHistory();

app.js

import { Router } from 'react-router-dom';
import history from './history';

const Login = () => {
    return <div>Login</div>;
};

export default () => (
    <Router history={ history }>
        <Route path="/login" component={Login} />
    </Router>
);

xxx.js

import history from './history';
   
history.push('/login'); // 这里使用不会有问题 

说实话,我也遇到这个问题了,目前没找到原因。最后想到一个小办法就是路由变化之后异步刷新当前页面。。。

history.push('/somePath'); // 路由发生变化
setTimeout(() => {
    window.location.reload();
}, 0);
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏