React Hook 用于重新渲染由异步递归函数更新的状态

React Hook for re-rendering state updated by asynchronous recursive function

我正在构建一个 React 应用程序来解决数独游戏并可视化解决算法。

我正在尝试将 class 组件转换为功能组件,但不确定如何使状态更改生效。

有问题的代码是:

  async solve() {
    await this.sleep(0);
    const board = this.state.board;
    for (let row = 0; row < 9; row++) {
      for (let col = 0; col < 9; col++) {
        if (board[row][col] !== 0) {
          // can't change filled cells
          continue;
        }
        // try 1 to 9 for the empty cell
        for (let num = 1; num <= 9; num++) {
          if (App.isValidMove(board, row, col, num)) {
            // valid move, try it
            board[row][col] = num;
            this.setState({ board: board });
            if (await this.solve()) {
              return true;
            } else {
              // didn't solve with this move, backtrack
              board[row][col] = 0;
              this.setState({ board: board });
            }
          }
        }
        // nothing worked for empty cell, must have deprived boards solution with a previous move
        return false;
      }
    }
    // all cells filled
    return true;
  }

(Full code hereapp is hosted here)

这里我需要async所以我可以使用sleep()来可视化算法。我使用 this.setState({ board: board }); 来在每次棋盘发生变化时触发重新渲染。


尝试转换为功能组件时,我尝试了:


我的问题是:

你可以在每次棋盘改变时使用一个效果,这个效果会运行。当没有更多的动作时,棋盘不会改变,效果也不会再运行。

以下是如何执行此操作的示例:

const { useState, useEffect } = React;
const getCell = (board) => {
  const row = board.findIndex((row) =>
    row.some((cell) => !cell.checked)
  );
  if (row === -1) {
    return [-1, -1, false];
  }
  const col = board[row].findIndex((cell) => !cell.checked);
  return [row, col, true];
};
const initialState = [
  [{ checked: false }, { checked: false }],
  [{ checked: false }, { checked: false }],
];
const App = () => {
  const [board, setBoard] = useState(initialState);
  useEffect(() => {
    const timer = setTimeout(
      () =>
        setBoard((board) => {
          const [row, col, set] = getCell(board);
          if (set) {//do we need to set board (there are moves)
            const boardCopy = [...board];
            boardCopy[row] = [...board[row]];
            boardCopy[row][col] = {
              ...boardCopy[row][col],
              checked: true,
            };
            return boardCopy;
          }
          //this will return the same as passed in
          //  so App will not re render because board
          //  will not have changed
          return board;//no moves left because set is false
        }),
      1000
    );
    return () => clearTimeout(timer);
  }, [board]);
  return (
    <div>
      <button onClick={() => setBoard(initialState)}>
        reset
      </button>
      <table>
        <tbody>
          {board.map((row, i) => (
            <tr key={i}>
              {row.map(({ checked }, i) => (
                <td key={i}>{checked ? 'X' : 'O'}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

已使用 useState 挂钩。

问题是我没有正确抄板;需要使用 const boardCopy = [...board];(而不是 const boardCopy = board)。

Full code of the functional component is here.

Previous class component code is here.