Search code examples
reactjsuse-effectuse-state

Parent component doesn't change from child component


I am trying to make a Sudoku solver. I have a parent component Board and a child component Possible which shows available options for any box in the board. I passed the state of board,selected(selected box position in the board) and function to update board as props. But when I try to change board from Possible it doesn't change unless the selected box selected is changed from parent component. I am trying to change board element from child component.

Here is my Board component.

import React, { useState } from 'react';
import Possibles from './avails.jsx';
import solve, { initBoard } from '../help';

function Board() {
  const [msg, setmsg] = useState('');
  const [solved, setSolved] = useState(false);
  const [grid, setGrid] = useState(initBoard());

  const [selected, setSelected] = useState([0, 0]);

  const solveBoard = () => {
    const solution = solve(grid);
    setGrid(solution);
    setSolved(true);
    let a = true;
    for (let i = 0; i < 9; i++) {
      for (let j = 0; j < 9; j++) {
        // console.log(i,j)
        if (grid[i][j] === 0) {
        //   console.log(grid[i][j]);
          a = false;
        }
      }
    }
    if (!a) {
      setmsg('Invalid Board!');
    } else setmsg('Here is your solution!');
  };
  const updatePosition = (row, col) => {
    setSelected([row, col]);
  };
  const resetBoard = () => {
    setGrid(initBoard());
    setSolved(!solved);
    setmsg('');
  };
  return (
    <div className="board">
      Sudoku Solver
      {grid.map((row, index) => (
        <div key={index} className="row">
          {row.map((el, i) => (
            <button
              type="button"
              key={i}
              className={selected[0] === index && selected[1] === i ? 'el selected' : 'el'}
              onClick={() => { updatePosition(index, i); }}
            >
              {el === 0 ? '' : el}
            </button>

          ))}
        </div>
      ))}
      {setSolved
        ? <Possibles board={grid} pos={selected} setGrid={setGrid} setPos = {setSelected}/> : ''}
      <button type="button" className="btn" onClick={solveBoard}>Solve</button>
      <button type="button" className="btn" onClick={resetBoard}>Reset</button>
      <div>{msg}</div>
    </div>
  );
}

export default Board;

And here is my child component Possibles.

import React, { useState, useEffect } from 'react';
import { getAvailableNumbers } from '../help';

function Possibles({ board, pos, setGrid }) {
  const [possibles, setPosibles] = useState([]);

  useEffect(() => {
    const avails = getAvailableNumbers(board, pos);
    avails.unshift(0);
    setPosibles(avails, board, possibles);
  }, [pos,board]);

  const updateGrid = (opt) => {
    // setPosibles((prev) => prev.filter((x) => x !== opt || x === 0));
    board[pos[0]][pos[1]] = opt;
    setGrid(board);
  };
  return (
    <div>
      {possibles.map((opt) => (
        <button type="button" key={opt} onClick={() => { updateGrid(opt); }}>{opt === 0 ? 'X' : opt}</button>
      ))}

    </div>
  );
}

export default Possibles;

Solution

  • please change your 'updateGrid' function to -

    const updateGrid = (opt) => {
        // setPosibles((prev) => prev.filter((x) => x !== opt || x === 0));
        board[pos[0]][pos[1]] = opt;
        board = [...board]; // this will make parent component rerender 
        setGrid(board);
      };
    

    input to setPosibles should be an array according to first line of 'Possibles' component -

    setPosibles(avails, board, possibles);
    

    Edit - react basically uses shallow comparison for props, state to detect new available change. By using a spread operator we are a creating new array with a new memory location, this let react know about new change and rerendering is triggered.