React:告诉子组件“重新初始化”,即使传递的道具相同

Posted

技术标签:

【中文标题】React:告诉子组件“重新初始化”,即使传递的道具相同【英文标题】:React: Tell child component to "reinitialize," even when the passed props are the same 【发布时间】:2021-04-28 23:56:12 【问题描述】:

我有一个呈现 Timer 组件的 MyComponent。我目前的设置是这样的:

MyComponent.render:

render () 
  return <Timer time=this.state.time lag=this.lag || 0 />

计时器:

class Timer extends Component 
  constructor(props) 
    super(props);
    this.state = 
      time: this.props.time,
    ;
  

  startTimer = (duration) => 
    if (duration > 0)
      this.on = true;
      let timer = duration * 1000 + this.props.lag;
      var s = setInterval(() => 
        this.setState(time: Math.round(timer/1000));
        timer = timer - 500;
          if (timer <= 0) 
            this.on = false;
            clearInterval(s);
          
      , 500);
    
  

  componentDidMount = () => 
    this.startTimer(this.props.time);
  

  render() 
    return (
      <div className="Timer-container">
        <div className="Timer-value">this.state.time</div>
      </div>
    );
  

如您所见,当 Timer 初始化时,它立即开始倒计时。在MyComponent 的后续渲染中,我想重新启动Timer,即使time 道具没有改变。换句话说,我希望它在每次渲染时“重新初始化”。我如何做到这一点?

【问题讨论】:

你试过从render()中调用函数吗? @Justinas 我不确定你的意思;你能详细说明一下吗? 内部渲染:clearInterval(this.s); this.startTimer(1000) 不要以为你想放入render(),因为它会在每次本地状态变化​​时调用该函数,我认为你可以调用componentWillReceiveProps中的函数,它会跳过本地状态,但只听父母重新渲染 作为一种解决方法,您可以将time 包装到一个对象中,每次您想重新启动计时器时,您都会传递一个新对象,即使在同一时间time=value:500 并且在组件中读取的是作为props.time.value。您可能需要重构代码,以便停止旧计时器(当然,如果您想停止它) 【参考方案1】:
    首先,要重置计数器,需要在状态中存储一些东西
间隔(以便您清除它) 或当前时间(因此您可以将其设置为初始值)。
    如果您想在父级重新渲染(但道具没有改变)时做某事,基本上您需要检查的是为什么您的组件更新了。答案是"Trace why a React component is re-rendering"

您的示例的一种快速方法是检查状态是否已更改(不推荐)

componentDidUpdate(prevProps, prevState, snapshot) 
    if( prevState === this.state )
        clearInterval( this.state.interval );
        this.startTimer( this.props.time );
    

另一个快速解决方案是(如果您可以选择)将shouldRerender 属性传递给组件,然后在组件内检查此属性:

// -- inside MyComponent
render () 
  return <Timer
    time= this.state.time 
    lag= this.lag || 0 
    shouldRerender= /* just an empty object */  />;


// -- inside Timer
componentDidUpdate(prevProps, prevState, snapshot) 
    if( prevProps.shouldRerender !== this.props.shouldRerender )
        clearInterval( this.state.interval );
        this.startTimer( this.props.time );
    

对我来说这看起来有点“脏”。一种更简洁的方法是将一些状态传递给shouldRerender,它会随着每次更新而改变(例如,只是增加数字)。

但是,我认为检查父级是否呈现的方法是不是 React 方式。我个人确实会考虑组件是否渲染一个实现细节(我不知道这样说是否正确),也就是说,我不在乎 React 何时决定渲染,我只关心道具和状态(基本上)。

我建议您考虑一下“因果”究竟是什么,您想要重置计时器的原因是什么。父母的重新渲染可能只是其他原因的影响,您也可以将其用于时间重置。

这里有一些不同的概念可能对我可以想象的用例有用:

不使用一个 Time 实例,而是在需要时在父级内部销毁和创建,也可以使用 key 道具。 使用HOC(如withTimer)或custom hook(如useTimer),注入reset()函数(另外创建一个单独的TimerView组件) 保持time状态在MyComponent,将timeonChange向下传递到Timer组件(&lt;Timer time= this.state.time onChange= time =&gt; this.setState( time: time ); /&gt;),然后MyComponentTimer都可以设置/重置时间.

【讨论】:

以上是关于React:告诉子组件“重新初始化”,即使传递的道具相同的主要内容,如果未能解决你的问题,请参考以下文章

React 啥时候重新渲染子组件?

在 React 中通过更改父级的道具来更新子组件

React:将状态传递给子组件

前端开发React 中父子组件之间的通信方式

react 父组件向子组件传递参数

React组件间信息传递方式