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:
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);
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}/>
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
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);
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.