Search code examples
reactjsreact-hooksreact-componentreact-functional-componentuse-state

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


I'm building a react app which solves a sudoku board and visualises the solving algorithm.

I'm trying to convert the class components to functional components, but am not sure how to get the state changes working.

The problematic code is:

  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 here and the app is hosted here)

Here I need async so I can use sleep() to visualise the algorithm. I use this.setState({ board: board }); to trigger a re-render everytime the board is mutated.


When trying to convert to a functional component I tried:

  • useState hook, I used const [board, setBoard] = useState(initialBoard); and replaced the this.setState calls with setBoard(board). This didn't work because the hook is being called in a loop
  • wrapping useState with useEffect (i.e. useEffect(() => setBoard(board), [board])). This didn't compile, gets the error React Hook "useEffect" may be executed more than once...
  • also looked into useReducer but have read it doesn't work well with async

My questions are:

  • Is this even possible to convert to a functional component?
  • If yes, what hook should I use and would it require a redesign of my solve() function?
  • Is it even best to convert this from a class component to a functional component?

Solution

  • Got it working with useState hook.

    The issue was I wasn't copying the board correctly; needed to use const boardCopy = [...board]; (rather than const boardCopy = board).

    Full code of the functional component is here.

    Previous class component code is here.