16

In React router, I have to and onClick attributes as shown below

<li key={i}><Link to="/about" onClick={() => props.selectName(name)}>{name}</Link></li>

state = {
    selectedName: ''
};

selectName = (name) => {
   setTimeout(function(){this.setState({selectedName:name});}.bind(this),1000);
   // this.setState({selectedName: name});
}
  • to attribute navigates to about Route
  • onClick assigns value to state variable selectedName which will be displayed when navigated to About page.

When I give timeout insided function called on click, its navigating to new page and after sometime state is getting updated resulting in displaying previous name until state is updated with new name.

Is there a way where it will navigate to the new route only after the code in onClick function gets executed.

You can get the entire code [here].(https://github.com/pushkalb123/basic-react-router/blob/master/src/App.js)

Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
crazyCoder
  • 329
  • 1
  • 2
  • 16

2 Answers2

16

One possible way is, Instead of using Link, use history.push to change the route dynamically. To achieve that remove the Link component and define the onClick event on li. Now first perform all the task inside onClick function and at the end use history.push to change the route means to navigate on other page.

In your case change the router inside setState callback function to ensure that it will happen only after state change.

Write it like this:

<li key={i} onClick={() => props.selectName(name)}> {name} </li>

selectName = (name) => {
    this.setState({ selectedName:name }, () => {
        this.props.history.push('about');
    });
}

Check this answers for:

When to use setState callback

How to navigate dynamically using react router dom

Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • 1
    With the new changes, its showing the error 'Cannot read property 'push' of undefined'. how to pass history as prop. I am using render inside as shown below. } /> – crazyCoder Jan 17 '18 at 06:57
  • use this: ` } />`, it will work, notice `{...props}` part :) – Mayank Shukla Jan 17 '18 at 07:00
  • check this answer for more details about `{...props}`: [**React react-router-dom pass props to component**](https://stackoverflow.com/questions/43469071/react-react-router-dom-pass-props-to-component/43469277#43469277) – Mayank Shukla Jan 17 '18 at 07:01
  • selectName() is in parent class (BasicExample) I tried with passing to Router as shown below but it didnt work. `selectName = (name) => { setTimeout(() => this.setState({ selectedName: name }, () => { this.props.history.push('about') }), 1000); } render(props) { return ( < Router {...props}>` Is there a way to access history in top most parent class – crazyCoder Jan 17 '18 at 08:27
  • 1
    wrap your component by `withRouter`, it will provide access to history, like this: `export default withRouter(ComponentName)`. [**Check DOC**](https://reacttraining.com/react-router/web/api/withRouter) – Mayank Shukla Jan 17 '18 at 08:33
  • If I wrap my component by `withRouter` , its throwing error **_You should not use or withRouter() outside a _** – crazyCoder Jan 17 '18 at 09:24
  • 1
    Thanks for the solution. Its working fine. Since the export statement was outside withRouter, as discussed I moved the Router to index.js file and it worked. Complete solution is updated [here](https://github.com/pushkalb123/basic-react-router/) – crazyCoder Jan 17 '18 at 10:48
3

Alternatively, I would recommend using URL Params in order to capture the name of the person that the about page is about. Thus, instead of the url being /about and the name being behind the scenes, it would be /about/tom or /about/pushkal. The way that you do this is by defining params in the URL router as such in your index.js:

<Route path="/about/:name" component={AboutPage}>

Now, when you link to the about page, you would do it as such:

<Link to={"/about/" + name}>{name}</Link>

Now, in your AboutPage component, you can access the name param as a prop in this.props.params.name. You can look at more examples here.

This method is a bit different than your current approach but I suspect it will lead to easier design later on

Rohan Varma
  • 1,145
  • 6
  • 11
  • 1
    This approach is solves the above purpose. The only change required is usage of {match} in About function. `const About = ({ match }) => (

    About {match.params.id}

    ` There is one limitation in this apporach if we need to store huge data and display huge data in the About page, passing all the data in URL is cumbersome.
    – crazyCoder Jan 17 '18 at 09:14