在SocketIO事件监听器中调用setState后,React Component无法更新

时间:2019-10-16 06:48:05

标签: javascript node.js reactjs socket.io

我有一个课堂组件。我已经在构造函数中添加了这一行:

this.handleChangedCells = this.handleChangedCells.bind(this);

我在componentDidMount中注册一个SocketIO侦听器,并调用类似的函数

socket.on("changedCells", this.handleChangedCells);

我正在以控制台方式接收数据。一旦收到,请记录下来。问题在于该函数调用this.setState且不会更新组件。我有

handleChangedCells(data) {
    console.log(data);
    let new_board = this.state.board;
    data.map(arr => {
      arr.map(obj => {
        new_board[obj.ind[0]][obj.ind[1]] = {
          color: obj.color,
          alive: obj.alive
        };
      });
    });

    this.setState({ board: new_board });
}

我拥有的组件如下:

render() {
    return (
      <div className="boardContainer" style={container_style}>
        {this.state.board.map((row, r_ind) => {
          return (
            <div className="row" key={r_ind}>
              {row.map((col, c_ind) => {
                return (
                  <Cell
                    key={[r_ind, c_ind]}
                    cell_row_index={c_ind}
                    cell_col_index={r_ind}
                    socketProps={socket}
                    colorProps={this.state.color}
                    clickedColorProps={col.color}
                    aliveProps={col.alive}
                  />
                );
              })}
            </div>
          );
        })}
      </div>
    );
  }

如您所见,该组件取决于我更新的状态,但是该组件未更新...对这里发生的事情有任何了解吗?

编辑:这是指向我的项目https://github.com/Dobermensch/ExpressTest.git的链接,相关文件为/client/components/Game.js

编辑2 :木板是包含对象{color:[x,y,z],alive:boolean}的数组的数组,我正在获取已更改的单元格(死单元格)和标记它们在董事会中已失效,因此理想情况下,我将失效细胞的索引移至董事会,并将其标记为已失效[row] [col] = {color:[x,y,z],live:false}

编辑3 :电路板的数据结构为[[{{.. {},{}],[{},{},{}]],并且我正在更改其中对象的属性数组

2 个答案:

答案 0 :(得分:1)

当我同时使用嵌套的objectsarray时遇到了类似的问题。我认为setState不能很好地处理嵌套更新。

另一个问题在您的代码中。您实际上是在突变state

let new_board = this.state.board;这里的木板是array。 JavaScript不会深度克隆arrayobject。您正在将refererence board中的array分配给新变量。

因此,您可以做的只是deep clone董事会array

let new_board = this.state.board.map(item => {...item}); // You may need to handle the multiple levels.

JSON.stringyJSON.parse也应该起作用。

let new_board = JSON.parse(JSON.stringify(array));

一旦您拥有deep clone中的state。您可以修改数据,setState应该触发重新渲染。

更新1:

当您使用单元状态更新它时。道具值在重新渲染时不会更新。

因此,您应该在Cell组件中使用getDerivedStateFromProps,以使prop的数据可供state使用。

static getDerivedStateFromProps(props, state) {
    return {
      r: props.clickedColorProps[0],
      g: props.clickedColorProps[1],
      b: props.clickedColorProps[2],
      alive: props.aliveProps
    };
  }

注意getDerivedStateFromProps将在组件的每次重新渲染时执行,即使状态改变也是如此。因此,方法中应该有一个条件,可以返回正确的数据。

答案 1 :(得分:0)

请不要使用JSON.parse进行深层复制,这是一个非常糟糕的主意,因为您要为所有内容创建新的对象,而不仅仅是为变化的对象创建新的对象。如果您知道什么是纯组件,那么您就会知道JSON.parse为什么不好。

您可以使用以下代码仅更改需要更改的内容:

handleChangedCells(data) {
  console.log(data);
  let new_board = [...this.state.board];
  data.forEach(arr => {
    arr.forEach(obj => {
      //shallow copy only if it's not copied already
      if (
        new_board[obj.ind[0]] ===
        this.state.board[obj.ind[0]]
      ) {
        new_board[obj.ind[0]] = [
          ...new_board[obj.ind[0]],
        ];
      }
      new_board[obj.ind[0]][obj.ind[1]] = {
        color: obj.color,
        alive: obj.alive,
      };
    });
  });
  this.setState({ board: new_board });
}

如果它仍然“不起作用”,那么我建议创建一个sandbox来说明问题。除了实际的xhr,您还可以返回更改后的样本数据。

其工作示例为here