如何在路由更改时卸载组件
How to unmount a component on route change
我在给定的路线上有一个组件,比如 app.com/cars/1
我有一个侧边栏,其中包含指向不同汽车的链接,例如 /cars/2
、/cars/3
等
我遇到的问题是当您更改链接时,比如从 cars/1
转到 cars/2
,组件没有卸载,我被 componentWillReceiveProps
解雇了。如果我转到另一个包含不同组件的页面,比如 /trucks
,该组件已卸载,一切正常。
如何在路由改变时卸载我的组件?我想要为下一辆车清除各种状态和通量的东西。或者,如果不卸载,人们是否有处理此类问题的典型方式?我无法想象这不是很常见。
(注意我使用的是 react-router)
我认为处理此问题的正常方法是在 componentWillReceiveProps
中注销和重新注册您的侦听器、重置您的状态等。围绕此行为创建抽象是正常的:
componentWillMount: function() {
this.setupStuff(this.props);
}
componentWillUnmount: function() {
this.tearDownStuff();
}
componentWillReceiveProps: function(nextProps) {
this.tearDownStuff();
this.setupStuff(nextProps);
}
setupStuff: function(props) {
this.setState(this.getDataFromStore(props.store));
props.store.listen(this.handler); // or whatever
}
tearDownStuff: function(props) {
props.store.unlisten(this.handler); // or whatever
}
但是,如果您真的想重新安装组件,可以使用几个选项。
如果您不希望 任何 组件在路由更改时保持安装状态,您可以利用路由器的 the createElement
option 添加唯一键组件:
function createElement(Component, props) {
var key = ...; // some key that changes across route changes
return <Component key={key} {...props} />;
}
// ...
<Router createElement={createElement}>
...
不过,我不推荐这样做。它不仅会使您的应用变慢,因为 每个 路由组件每次都会重新安装,而且还会完全禁用具有不同道具的同一路由处理程序的后续渲染之间的动画。
如果您只想让 某些 路由始终重新渲染,您可以通过 React.cloneElement
:
在父级中给它一个键
render: function() {
var key = ...; // some key that changes across route changes
return React.cloneElement(
React.Children.only(this.props.children),
{key: key}
);
}
我最后只是做了:
const createElement = (Component, props) =>
<Component key={props.params.id} {...props}/>;
ReactDOM.render(
<Router history={browserHistory} createElement={createElement}>
<Route path="courses/:id" component={Page_courses_id}/>
</Router>
);
并忽略潜在的性能问题(如果它们曾经发生过),在我看来维护成本(重置 :dynamic-segment 下所有组件的状态,在 componentWillReceiveProps 中重新获取数据等)是不值得的。
我选择了带有更改密钥的 Michelle Tilley 选项,但我没有为路由器中的整个组件设置密钥,而是只为需要通过重新启动挂钩进行更新的组件设置密钥。
效果很好。
const UniqComponent = () => {
const uniqId = 123; // may be carId
return <div key={uniqId} />
}
ReactDOM.render(
<Router history={browserHistory}>
<Route path="cars/:id" component={UniqComponent}/>
</Router>
);
我在给定的路线上有一个组件,比如 app.com/cars/1
我有一个侧边栏,其中包含指向不同汽车的链接,例如 /cars/2
、/cars/3
等
我遇到的问题是当您更改链接时,比如从 cars/1
转到 cars/2
,组件没有卸载,我被 componentWillReceiveProps
解雇了。如果我转到另一个包含不同组件的页面,比如 /trucks
,该组件已卸载,一切正常。
如何在路由改变时卸载我的组件?我想要为下一辆车清除各种状态和通量的东西。或者,如果不卸载,人们是否有处理此类问题的典型方式?我无法想象这不是很常见。
(注意我使用的是 react-router)
我认为处理此问题的正常方法是在 componentWillReceiveProps
中注销和重新注册您的侦听器、重置您的状态等。围绕此行为创建抽象是正常的:
componentWillMount: function() {
this.setupStuff(this.props);
}
componentWillUnmount: function() {
this.tearDownStuff();
}
componentWillReceiveProps: function(nextProps) {
this.tearDownStuff();
this.setupStuff(nextProps);
}
setupStuff: function(props) {
this.setState(this.getDataFromStore(props.store));
props.store.listen(this.handler); // or whatever
}
tearDownStuff: function(props) {
props.store.unlisten(this.handler); // or whatever
}
但是,如果您真的想重新安装组件,可以使用几个选项。
如果您不希望 任何 组件在路由更改时保持安装状态,您可以利用路由器的 the createElement
option 添加唯一键组件:
function createElement(Component, props) {
var key = ...; // some key that changes across route changes
return <Component key={key} {...props} />;
}
// ...
<Router createElement={createElement}>
...
不过,我不推荐这样做。它不仅会使您的应用变慢,因为 每个 路由组件每次都会重新安装,而且还会完全禁用具有不同道具的同一路由处理程序的后续渲染之间的动画。
如果您只想让 某些 路由始终重新渲染,您可以通过 React.cloneElement
:
render: function() {
var key = ...; // some key that changes across route changes
return React.cloneElement(
React.Children.only(this.props.children),
{key: key}
);
}
我最后只是做了:
const createElement = (Component, props) =>
<Component key={props.params.id} {...props}/>;
ReactDOM.render(
<Router history={browserHistory} createElement={createElement}>
<Route path="courses/:id" component={Page_courses_id}/>
</Router>
);
并忽略潜在的性能问题(如果它们曾经发生过),在我看来维护成本(重置 :dynamic-segment 下所有组件的状态,在 componentWillReceiveProps 中重新获取数据等)是不值得的。
我选择了带有更改密钥的 Michelle Tilley 选项,但我没有为路由器中的整个组件设置密钥,而是只为需要通过重新启动挂钩进行更新的组件设置密钥。
效果很好。
const UniqComponent = () => {
const uniqId = 123; // may be carId
return <div key={uniqId} />
}
ReactDOM.render(
<Router history={browserHistory}>
<Route path="cars/:id" component={UniqComponent}/>
</Router>
);