从setState()更新视图不​​会在预期时触发重新渲染

时间:2016-01-09 19:40:55

标签: javascript react-jsx

答案: 我是个傻瓜。这是迟到的,但不想留下未回复的帖子,特别是因为我最初的回答是错误。我需要引用我得到的 new 道具,而不是 this.props 。像往常一样,答案是documentation。我在下面更新了自己的答案以反映这一点。

编辑1: 我的第一个小提琴没有完全显示我的问题,所以我updated it更好地证明了这一点。出于某种原因,当我调用 setState()时,我认为第一次通过它是未定义的,即使它给定了一个值,但在后续传递中它按预期工作。看起来我的初始 setState()调用没有触发重新渲染,但其他所有调用都是。

与通常的" setState不更新视图有点不同"问题为 setState()正在更新我的视图并使用正确的值。就在我不期待的时候。基本上我触发一个setState()事件应该用新的道具重新渲染我的子组件,我相信它应该触发子组件componentWillReceiveProps lifecyle事件,然后在子组件中调用 setState()并更新它视图。问题是,当它确实更新视图时,它似乎在预期时落后一个循环。在我的MVP中,我在2秒时调用 setState(),但它在4秒时更新了视图。我虽然无法确定哪个部分是错误的逻辑。

这是我的jsFiddle MVP。感谢您的任何建议。

代码:

class TaskBody extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  componentWillReceiveProps() {
    this.setState({
      activeTask: this.props.activeTask
    });
  }
  render() {
    return <div>
    < h4 > {
        this.state.activeTask ? this.state.activeTask : 'Task Title'
      } < /h4>
  < /div > ;
}
}

class Body extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeTask: '',
      counter: 1
    };
    this.updateActive = this.updateActive.bind(this);
  }
  updateActive(task) {
    this.setState({
      activeTask: task
    });
  }
  componentDidMount(){
    var self = this;
    setInterval(function(){
        if(self.state.counter === 4){
            clearInterval(self.clickInterval);
            return clearInterval(self.countInterval);
        }
        self.setState({ counter: self.state.counter + 1 });
    }, 1000);
    // imagine this was the click event, it should rerender the view
    // instantaneously at 2 seconds because I called setState() right?
    // which is supposed to trigger a re-render and then TaskBody should
    // receive new props triggering it to setState() on its activeTask,
    // which should update the view?
    self.clickInterval = setInterval(function(){
        self.setState({ activeTask: 'took double the time it should' });
    }, 2000);
  }
  render() {
    return <div>
        < TaskBody activeTask = {
        this.state.activeTask
      }/> 
      <div>{this.state.counter}</div>
      </div>;
  }
}

ReactDOM.render(<Body />, document.querySelector('#body'));

2 个答案:

答案 0 :(得分:0)

self.setState({ activeTask: 'took double the time it should' });

实际上从未被调用过。

原因如下:

您的逻辑位于componentDidMount生命周期方法中。如果您阅读the documentation of componentDidMount,您会看到它明确指出:

  

立即仅在客户端(不在服务器上)调用一次   初始渲染发生后。

因此,当您在应用中调用componentDidMount时,您首先会检查计数器并在那里调用setState。仅这一点就会触发一个新的渲染。这意味着componentDidMount中的第二个代码块将无效,因为在设置方法时,它永远不会被调用。

答案 1 :(得分:0)

在我的旧答案中,我使用setTimeout()作为黑客来获得我想要的结果。基本上,如果我在超时中包装我的setState,它将使用新的道具设置状态,但如果我没有,它仍然会引用旧的道具。幸运的是,这已经在react中处理,因为componentWillReceiveProps默认已经接收到newProps参数。

此:

componentWillReceiveProps(){
      var self = this;
      setTimeout(function(){
        self.setState({activeTask: self.props.activeTask});
      },0);
}

变为

componentWillReceiveProps(newProps){
      var self = this;
      self.setState({activeTask: newProps.activeTask});
}