14

I am duffer when it comes to React-Router component. However I was not able to find explanation of why my components become unmount when I walk through links? And how to prevent it ?

In my example I have a component that contains timer and re-render content by

I got an error:

enter image description here

Here is ReactJS code :

/*global define, Backbone, React, $, Request, Router, Route, Link */

var App = React.createClass({
    render: function () {
        return (
            <div>
                <h1>App</h1>
                <ul>
                    <li><Link to="/about">About</Link></li>
                    <li><Link to="/timer">Timer</Link></li>
                </ul>
                {this.props.children}
            </div>
        )
    }
});

var About = React.createClass({
    render: function () {
        return <h3>Here is about page</h3>
    }
});

var Timer = React.createClass({
    getInitialState: function() {
        return {counter: 0};
    },
    render: function () {
        return (
            <div>
                <h2>Time is running over...</h2>
                <b>{this.props.interval}</b>
                <p>{this.state.counter}</p>
            </div>
        )
    },
    componentDidMount: function () {
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer, 1000);
    },
    loadCommentsFromServer: function () {
        this.setState({counter: this.state.counter + 1});
    }
});

React.render((
    <Router location="history">
        <Route path="/" component={App}>
            <Route path="about" component={About} />
            <Route path="timer" component={Timer} />
        </Route>
    </Router>
), document.body);
AlexeiBerkov
  • 427
  • 1
  • 6
  • 16
  • Do you want the timer to be active throughout your whole app? Why do you have a specific route for it? – deowk Oct 30 '15 at 09:03
  • 1
    It is just an example. My main aim is do not unmount components during navigate through the links. In my real project I have a complicated component with huge grid. So every time I go back to this component it sends request to server to get data. But I do not need such behavior. And I do not understand why React-Router destroy component instead of just hide it ? – AlexeiBerkov Oct 30 '15 at 09:06
  • 1
    react-router is behaving as expected in this case - if you have a component that should not unmount then you need to compose it into your top level app component - see my answer below – deowk Oct 30 '15 at 09:11

3 Answers3

8

If you want show component again without unmount you can show it always and hide when routes leave. To achieve this place you competent outside target route, for example you want prevent ProjectsList from unmout:

   <Route path="/" component={App}>
        <IndexRoute component={ProjectsList} />
        .....

1. Place ProjectsList into App and create such proxy component instead component={ProjectsList}:

  const LayoutToogler = () => (<div></div>);

Will look so:

    <Route path="/(mobile.html)" component={App}>
        <IndexRoute component={LayoutToogler} showProjects={true}/>
  1. In top level App component just check this property (showProjects) to decide show projects or not:

           <ProjectsList
             style={{display:this.props.children.props.route.showProjects?'block':'none'}}
                    />
    

Don't forget to handle style property in your ProjectList component

basil
  • 3,482
  • 2
  • 22
  • 20
3

in your case react-router is working as expected, if you want the timer to be visible throughout your app then you need to treat is a component and not a view

/*global define, Backbone, React, $, Request, Router, Route, Link */

var App = React.createClass({
    render: function () {
        return (
            <div>
                <h1>App</h1>
                // this will not unmount when routes are changed
                <Timer /> 
                <ul>
                    <li><Link to="/about">About</Link></li>
                </ul>
                // this will unmount/mount when routes are changed
                {this.props.children}
            </div>
        )
    }
});

var About = React.createClass({
    render: function () {
        return <h3>Here is about page</h3>
    }
});

var Timer = React.createClass({
    getInitialState: function() {
        return {counter: 0};
    },
    render: function () {
        return (
            <div>
                <h2>Time is running over...</h2>
                <b>{this.props.interval}</b>
                <p>{this.state.counter}</p>
            </div>
        )
    },
    componentDidMount: function () {
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer, 1000);
    },
    loadCommentsFromServer: function () {
        this.setState({counter: this.state.counter + 1});
    }
});

React.render((
    <Router location="history">
        <Route path="/" component={App}>
            <Route path="about" component={About} />
        </Route>
    </Router>
), document.body);
deowk
  • 4,280
  • 1
  • 25
  • 34
  • 5
    But in this case timer will always be visible. As I mentioned before in my real project I have a complicated component with huge grid. So every time I go back to this component it sends request to server to get data. But I do not need such behavior. And I do not understand why React-Router destroy component instead of just hide it ? Seems like I do not understand the purpose of using react-router :( – AlexeiBerkov Oct 30 '15 at 09:33
  • 2
    @AlexeiBerkov did you ever find a solution for this? – Rijk Feb 24 '17 at 22:07
  • 1
    I use simple CSS aproache – AlexeiBerkov Mar 07 '17 at 15:16
1

To avoid to fetch/construct a big/slow chunk of data when your component is mounted again by the router, you can do this: 1) implement a global cache object (just export an empty object from the root of your app at pure javascript level) 2) in you component, do:

import {cache} from '.....App';
const myComponent = props => {
if (!cache.myBigList) {
  cache.myBigList = [];
}
const [list, setList] = useState(cache.myBigList);
cache.myBigList = list;
...

This way, the very first time the component is mounted it will initiate the cache at []. Then every time list is changed the function is executed again and list is saved in cache. When component is mounted again, the initial state given to useState is the last saved state from the previous mounted cycle.

Fred
  • 521
  • 1
  • 4
  • 7