如何在路由更改时卸载组件

19

我有一个组件在给定的路由上,比如说app.com/cars/1

我有一个侧边栏,其中有指向不同汽车的链接,例如/cars/2, /cars/3等。

问题在于当您更改链接时,比如从cars/1cars/2,组件不会卸载,并且我会收到 componentWillReceiveProps 的通知。如果我转到另一个具有不同组件的页面,例如/trucks,则组件会被卸载,一切都很好。

如何在路由更改时卸载我的组件?我有各种状态和flux内容,我想要为下一个汽车清除它们。如果不能卸载,那么人们通常如何处理这种问题?我无法想象这不是非常普遍的情况。

(注意:我正在使用react-router)

3个回答

17
我认为正常处理的方法就是在 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
}

然而,如果您真的想重新安装组件,有几个选项可供选择。

如果您不希望在路由更改时保留任何组件,请利用路由器的createElement选项向组件添加唯一的键:

function createElement(Component, props) {
  var key = ...; // some key that changes across route changes
  return <Component key={key} {...props} />;
}

// ...

<Router createElement={createElement}>
  ...

然而,我不建议这样做。这不仅会使你的应用程序变慢,因为每次都会重新挂载每个路由组件,而且还会完全禁用在同一路由处理程序使用不同props进行后续渲染之间的动画效果。
如果你只想让特定的路由始终重新渲染,可以通过在父组件中使用React.cloneElement为其指定一个键值。
render: function() {
  var key = ...; // some key that changes across route changes
  return React.cloneElement(
    React.Children.only(this.props.children),
    {key: key}
  );
}

1
现在仍然相关,但是如果您的状态来自于您无法控制的钩子,则重置状态并不容易。 - cdosborn
@cdosborn 是的。我认为我可能会将钩子用法包装在一个子组件中,并根据包装组件中的其他数据需要使用一个变化的“key”属性进行渲染,以启用强制重新挂载。请参见https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key - Michelle Tilley

3
我最终只做了以下事情:
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>
);

忽略可能发生的性能问题(如果存在的话),在我看来,维护成本(重置动态片段下的所有组件状态,重新获取componentWillReceiveProps中的数据等)不值得这样做。

我有一个类似的情况......一个侧边栏可以在左侧打开和关闭,右侧有 dhtmlx-scheduler 组件。页面重新加载时一切正常,但当我转到 Route3 并回到侧边栏和 dhtmlx-cheduler 存在的 Route2 时,打开侧边栏时,调度程序组件应根据扩展侧边栏时剩余的宽度调整其宽度,但某些部分的调度程序被剪切了。对于硬刷新页面,虽然它可以正常工作,并在扩展侧边栏时完美地调整其宽度。我应该在每次路由更改时卸载调度程序组件吗? - sk215
请查看附加的Github链接 https://github.com/sonalk215/dhtml任何指针将非常有帮助 - sk215
@sk215,我相信你将更有运气将此作为问题提交。 - Evgenia Karunus
我提出了一个新问题,感谢lakesare的建议。问题链接 https://stackoverflow.com/questions/64563819/react-with-dhtmlx-scheduler - sk215

1
我选择了带有更改键的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>
);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接