React JSX:父组件重新渲染重新挂载子组件是否正常?
React JSX: is it normal that re-render of parent component remounts child component?
在我的 React 应用程序的根组件中,我有这个:
class App extends Component {
...
ComponentDidMount() {
window.addEventListener('resize', this.handleResize, false);
}
handleResize{
this.parentSize = {height: window.innerWidht - this.node.offsetLeft ..., width: ...};
this.forceUpdate();
}
render() {
<div ref="e=>this.node=e}>
<ChildComponent parentSize={this.ParentSize} />
</div>
}
}
我注意到当 App 组件通过 setState 或 forceUpdate() 重新呈现时,子组件实际上被重新挂载(因此每次更新都会调用 componentWillUnmount、componentWillMount、componentDidMount)。我期望的行为是再次调用 Child 组件的渲染函数。我最近升级到React-router 4.x,所以我不确定是否与此有关。
谁能告诉我这是否是设计使然?多次调用我的 componentDidMount 会导致一些问题,因为我试图在其中获取数据,并且我不想在调整大小期间多次执行该昂贵的操作。
更新:
我找到了这个问题的可能原因。我有多个嵌套组件,由于很难 post 整个项目,所以有时很难 post 导致问题的实际代码。但我认为就是这样:
return (
<div className="app-view" style={style} ref={el=>this.node=el}>
<Switch>
<Route exact path='/' component={() => <Home store={store} />} />
<Route path='*' component={Error404} />
</Switch>
</div>
);
这里我试图将 props 传递给路由中的组件 (React-router 4.x)
我通过提供一个创建主页的函数来做到这一点,这可能是在每次渲染时重新创建主页。
所以问题就变成了,如何在不在 Route 内每次渲染时重新创建页面的情况下传递道具?
在我看来,从您发布的代码来看,您提到的函数都改变 this.parentSize
然后重新呈现页面。事件如果 this.parentSize
的值没有改变,它的值正在被重新分配:
this.parentSize = {height: window.innerWidht - this.node.offsetLeft ..., width: ...};
如果我的理解是正确的,那么子组件是需要重新渲染的,因为用于生成组件的信息发生了变化,因此必须重新渲染组件以解决props的变化。很可能 DOM 没有变化,因此页面在视觉上没有变化。
您可以使用 Perf 插件和 printOperations
函数检查正在执行的操作。
防止重新渲染的一种方法是在您不希望组件重新渲染时使用 shouldcomponentupdate
和 return false
编辑:
根据上面提供的信息,您似乎想通过调用父组件上的函数来更新子组件,但又不想让父组件重新渲染?
如果是这种情况,您可以执行以下操作:
子组件第一次渲染时,你调用componentWillMount
,从父组件中获取你希望能够控制的道具,并将其保存为状态,如下所示:
componentWillMount() {
const { myProp } = this.props;
this.setState({
myProp
});
}
然后在这个组件中创建一个函数,名称如下:
updateMyProp(newProp) {
this.setState({
myProp: newProp
})
}
从此以后,这个变量由state
控制,而不是props
接下来,在父页面上,组件需要有一个ref
变量,像这样:
<Yourcomponent
ref={d=> this.componentName = d}
...
/>
现在,您可以通过执行以下操作从父页面调用 updateMyProp
函数:
this.componentName.updateMyProp(someNewValue)
这会导致子组件重新渲染,但父组件不会
希望我已经正确理解了你的问题!!
我已确认该问题与我最近的 React-Router 4.x 升级有关。
React-Router 的文档:
当您使用组件(而不是下面的 render 或 children)时,路由器使用 React.createElement 从给定的组件创建一个新的 React 元素。这意味着如果你为组件属性提供一个内联函数,你将在每次渲染时创建一个新组件。这会导致现有组件卸载并安装新组件,而不是仅仅更新现有组件。使用内联函数进行内联渲染时,请使用 render 或 children 属性(如下)。
感谢大家的支持
在我的 React 应用程序的根组件中,我有这个:
class App extends Component {
...
ComponentDidMount() {
window.addEventListener('resize', this.handleResize, false);
}
handleResize{
this.parentSize = {height: window.innerWidht - this.node.offsetLeft ..., width: ...};
this.forceUpdate();
}
render() {
<div ref="e=>this.node=e}>
<ChildComponent parentSize={this.ParentSize} />
</div>
}
}
我注意到当 App 组件通过 setState 或 forceUpdate() 重新呈现时,子组件实际上被重新挂载(因此每次更新都会调用 componentWillUnmount、componentWillMount、componentDidMount)。我期望的行为是再次调用 Child 组件的渲染函数。我最近升级到React-router 4.x,所以我不确定是否与此有关。
谁能告诉我这是否是设计使然?多次调用我的 componentDidMount 会导致一些问题,因为我试图在其中获取数据,并且我不想在调整大小期间多次执行该昂贵的操作。
更新:
我找到了这个问题的可能原因。我有多个嵌套组件,由于很难 post 整个项目,所以有时很难 post 导致问题的实际代码。但我认为就是这样:
return (
<div className="app-view" style={style} ref={el=>this.node=el}>
<Switch>
<Route exact path='/' component={() => <Home store={store} />} />
<Route path='*' component={Error404} />
</Switch>
</div>
);
这里我试图将 props 传递给路由中的组件 (React-router 4.x) 我通过提供一个创建主页的函数来做到这一点,这可能是在每次渲染时重新创建主页。
所以问题就变成了,如何在不在 Route 内每次渲染时重新创建页面的情况下传递道具?
在我看来,从您发布的代码来看,您提到的函数都改变 this.parentSize
然后重新呈现页面。事件如果 this.parentSize
的值没有改变,它的值正在被重新分配:
this.parentSize = {height: window.innerWidht - this.node.offsetLeft ..., width: ...};
如果我的理解是正确的,那么子组件是需要重新渲染的,因为用于生成组件的信息发生了变化,因此必须重新渲染组件以解决props的变化。很可能 DOM 没有变化,因此页面在视觉上没有变化。
您可以使用 Perf 插件和 printOperations
函数检查正在执行的操作。
防止重新渲染的一种方法是在您不希望组件重新渲染时使用 shouldcomponentupdate
和 return false
编辑:
根据上面提供的信息,您似乎想通过调用父组件上的函数来更新子组件,但又不想让父组件重新渲染?
如果是这种情况,您可以执行以下操作:
子组件第一次渲染时,你调用componentWillMount
,从父组件中获取你希望能够控制的道具,并将其保存为状态,如下所示:
componentWillMount() {
const { myProp } = this.props;
this.setState({
myProp
});
}
然后在这个组件中创建一个函数,名称如下:
updateMyProp(newProp) {
this.setState({
myProp: newProp
})
}
从此以后,这个变量由state
控制,而不是props
接下来,在父页面上,组件需要有一个ref
变量,像这样:
<Yourcomponent
ref={d=> this.componentName = d}
...
/>
现在,您可以通过执行以下操作从父页面调用 updateMyProp
函数:
this.componentName.updateMyProp(someNewValue)
这会导致子组件重新渲染,但父组件不会
希望我已经正确理解了你的问题!!
我已确认该问题与我最近的 React-Router 4.x 升级有关。 React-Router 的文档:
当您使用组件(而不是下面的 render 或 children)时,路由器使用 React.createElement 从给定的组件创建一个新的 React 元素。这意味着如果你为组件属性提供一个内联函数,你将在每次渲染时创建一个新组件。这会导致现有组件卸载并安装新组件,而不是仅仅更新现有组件。使用内联函数进行内联渲染时,请使用 render 或 children 属性(如下)。
感谢大家的支持