React Router 4 and props.history.push

2019-02-26 03:06发布

问题:

There's something driving me crazy in React, and I need your help. Basically, when the user clicks "My Profile", I want to redirect to the user's profile. To do that, I use the following snippet

viewProfile = () => {
    console.log('My USER ID: ', this.props.userId);
    this.props.history.push(`/profile/${this.props.userId}`);
}

This should work. However, although the URL is changing to the correct URL when 'My Profile' is clicked, the page isn't appearing. It just stays as the old page.

After googling for a while, I know it's something to do with redux ... However, I can't find a solution. (this.props.userId is coming from a Layout component, which is in turn getting it from redux store)

Here's my code:

// React
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
// Redux
import { connect } from 'react-redux';
import * as actions from '../../../store/actions/';
// Containers and Components
   ...// more imports

const styles =  // styles

class NavBar extends Component {

  handleAuthentication = () => {
    if (this.props.isAuthenticated) {
      this.props.history.push('/logout');
    } else {
      this.props.history.push('/login');
    }
  };

  viewProfile = () => {
    console.log('My USER ID: ', this.props.userId);
    this.props.history.push(`/profile/${this.props.userId}`);
  };

  render() {
    let buttons = (
      <Auxillary>
        {this.props.isAuthenticated ? (
          <RaisedButton
            label="MY PROFILE"
            primary={true}
            style={styles.button}
            onClick={this.viewProfile} // THIS IS THE METHOD
          />
        ) : null}
        <RaisedButton
          backgroundColor={grey900}
          label={this.props.isAuthenticated ? 'LOGOUT' : 'LOGIN'}
          labelColor={grey50}
          style={styles.button}
          onClick={this.handleAuthentication}
        />
      </Auxillary>
    );

    let itemSelectField = null;
    if (this.props.location.pathname === '/items') {
      itemSelectField = (
        <ItemSelectField
          onSelectTags={tags => this.props.filterItemsByTagName(tags)}
        />
      );
    }
    let bar = null;
    if (
      this.props.location.pathname !== '/login' &&
      this.props.location.pathname !== '/register'
    ) {
      bar = (
        <AppBar
          style={styles.appBar}
          title={itemSelectField}
          iconElementLeft={
            <img style={styles.logoHeight} src={Logo} alt="logo" />
          }
          iconElementRight={buttons}
        />
      );
    }
    return <Auxillary>{bar}</Auxillary>;
  }
}

const mapDispatchToProps = dispatch => {
  return {
    filterItemsByTagName: selectedTags =>
      dispatch(actions.filterItemsByTagName(selectedTags))
  };
};

export default withRouter(connect(null, mapDispatchToProps)(NavBar));

You can find the entire app here: https://github.com/Aseelaldallal/boomtown/tree/boomtown-backend-comm

I'm going crazy trying to fix this. help.

回答1:

You need to integrate the react-router-redux.

Its look like the updated state is not reflecting in the container as redux do shallow comparison to check whether the component need to be update or not.

import {Router} from 'react-router-dom';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';

const history = createHistory()
const middleware = routerMiddleware(history)


const rootReducer = combineReducers({
    router: routerReducer,
  //other reducers
});

const store = createStore(rootReducer,applyMiddleware(middleware));

<Provider store={store}>
<ConnectedRouter>
 //routes
</ConnectedRouter>
</Provider>

Also,at reducers,add this snippet.

let updatedStore = JSON.parse(JSON.stringify(state));


回答2:

I think has to do with you using <BrowserRouter>. It creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>. Instead you can use ConnectedRouter and pass a history instance as

import createHistory from 'history/createBrowserHistory';
...
const history = createHistory();
...
<ConnectedRouter history={history}>
...
</ConnectedRouter>