Javascript 反应:告诉子组件“反应”;“重新初始化”;即使传递的道具相同

Javascript 反应:告诉子组件“反应”;“重新初始化”;即使传递的道具相同,javascript,reactjs,Javascript,Reactjs,我有一个MyComponent来呈现计时器组件。我当前的设置如下: MyComponent.render: render () { return <Timer time={this.state.time} lag={this.lag || 0} /> } render(){ 返回 } 计时器: class Timer extends Component { constructor(props) { super(props); this.state = {

我有一个MyComponent来呈现计时器组件。我当前的设置如下:

MyComponent.render:

render () {
  return <Timer time={this.state.time} lag={this.lag || 0} />
}
render(){
返回
}
计时器:

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>
    );
  }
}
类计时器扩展组件{
建造师(道具){
超级(道具);
此.state={
时间:这个。道具。时间,
};
}
起始时间=(持续时间)=>{
如果(持续时间>0){
this.on=true;
让定时器=持续时间*1000+this.props.lag;
var s=setInterval(()=>{
this.setState({time:Math.round(timer/1000)});
定时器=定时器-500;
中频(定时器){
this.startTimer(this.props.time);
}
render(){
返回(
{this.state.time}
);
}
}
如您所见,当计时器初始化时,它立即开始倒计时。在随后的
MyComponent
渲染中,我想重新启动
计时器
,即使
时间
道具没有改变。换句话说,我想在每次渲染时都“重新初始化”。我该如何实现这一点

  • 首先,要重置计数器,需要在状态中存储一些内容
    • 间隔(以便您可以清除它)
    • 或当前时间(因此可以将其设置为初始值)
  • 当你想做一些事情,如果父母重新呈现(但道具没有改变),基本上你需要检查的是为什么你的组件更新
  • 对于您的示例,快速方法是检查状态是否已更改(不推荐):

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

    // -- 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 );
        }
    }
    
    /--MyComponent内部
    渲染(){
    返回;
    }
    //--内部定时器
    componentDidUpdate(prevProps、prevState、快照){
    if(prevProps.shouldRerender!==this.props.shouldRerender){
    clearInterval(this.state.interval);
    this.startTimer(this.props.time);
    }
    }
    
    对我来说,这看起来有点“脏”。更干净的方法是将一些状态传递给
    shouldRerender
    ,该状态在每次更新时都会发生变化(例如,只是数量不断增加)

    <>但是,我认为检查父渲染的方法是<强>不是反应方式< /强>。我个人认为,如果一个组件渲染或没有实现细节(我不知道这是不是正确的话),也就是说,我不在乎什么时候反应决定渲染,我只关心道具和状态(基本上)。 我建议您考虑一下什么是“因果关系”,您想重置计时器的原因是什么。可能父对象的重新渲染只是其他原因的结果,您也可以使用这些原因重置时间

    下面是一些我能想象到的对用例有用的不同概念:

    • 不使用一次性实例,但在需要时在父级内部销毁和创建,也可以使用道具
    • 使用(如
      withTimer
      )或(如
      useTimer
      ),注入
      reset()
      函数(另外创建一个单独的
      TimerView
      组件)
    • time
      状态保持在
      MyComponent
      ,将
      time
      onChange
      传递到
      Timer
      组件(
      {this.setState({time:time});}}/>
      ),然后
      MyComponent
      Timer
      都可以设置/重置时间

    您是否尝试过从
    render()
    中调用函数?@Justinas我不确定您的意思;您能详细说明一下吗?内部render:
    clearInterval(this.s);this.startTimer(1000)
    您不想将其放入
    render()中
    ,因为它会在每次本地状态更改时调用函数,所以我认为您可以在
    componentWillReceiveProps
    中调用函数,它会跳过本地状态,但只侦听父级重新渲染解决方案您可以将
    时间
    包装到对象中,每次您想要重新启动计时器时,您都会传递一个新对象,甚至是同时
    time={value:500}
    在组件中读取为
    props.time.value
    。您可能需要重构代码,以便停止旧计时器(当然,如果您想停止它的话)
    // -- 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 );
        }
    }