Search code examples
javascriptreactjsreact-routerflux

React-router: true way to propagate route-component props/state


Question: isn't it an anti-pattern to pass component props/state via location.state? May you suggest a better way?

I have some social site, where each user may create his own profile. Each profile is an UserProfile component, which are routed like:

ReactDOM.render((
    <Router history={History}>
        <Route path="/" component={App}>
            <IndexRoute component={Welcome} />
            <Route path="profile" component={UserProfile} />
        </Route>
    </Router>
), document.getElementById('app'));

And I need to make a redirect on user click to specific user profile from multiple places of my site. I do that like:

// Where server response contains username, surname, some counters etc.
Service.getUserSummary(userId).then(response => {
  History.pushState(reponse, '/profile');
});

And retrieve response at UserProfile:

module.exports = React.createClass({

  render() {
    // QUESTION: isn't it an anti-pattern? Is there any better way?
    const state = this.props.location.state,
          username = state.username,
          ............
  }
})

Solution

  • If you deal with profiles distinguished by ID, the best way is to include the ID in the URL:

    ReactDOM.render((
        <Router history={History}>
            <Route path="/" component={App}>
                <IndexRoute component={Welcome} />
                <Route path="profile/:userId" component={UserProfile} />
            </Route>
        </Router>
    ), document.getElementById('app'));
    

    This id will then be available inside UserProfile as this.props.params.userId.

    A better practice is to load data from the server after the redirect, not before or during it. So, you have 3 stages of showing profile page:

    1. Just before the user clicked the link;
    2. User clicked the link, router has redirected to User Profile page, but it's still empty ("loading...");
    3. Data has been arrived from the server, state of UserProfile component has been updated, and it's re-rendered with the new data.

    In the simplest way the data can be retrieved in componentDidMount() method and set to the state (see https://facebook.github.io/react/tips/initial-ajax.html):

    var UserProfile = React.createClass({
      getInitialState: function() {
        return {
          data: null,
        };
      },
    
      componentDidMount: function() {
    
        // Your code for fetching user data from server:
        Service.getUserSummary(this.props.params.userId).then(response => {
          if (this.isMounted()) {
            this.setState({
              data: response.data
            });
          }
        });
      },
    
      render: function() {
        if (!this.state.data) {
    
            // Rendering stage (2)
            return (<div>Loading...</div>);
        }
    
        // Rendering stage (3)
        return (
          <div>
            I am {this.state.data.userName}!
          </div>
        );
      }
    });