Javascript 反应UseContext更改不重新呈现组件

Javascript 反应UseContext更改不重新呈现组件,javascript,reactjs,react-hooks,use-context,use-reducer,Javascript,Reactjs,React Hooks,Use Context,Use Reducer,我正在尝试使用React to learn UseContext和UseReducer制作一个简单的“Nonogram”/“Picross”游戏,但我不明白为什么我的顶级组件(应用程序)在使用的值发生变化时没有重新渲染。也许我遗漏了一些基本的东西,但我已经在网上阅读了文档和示例,不明白为什么不重新渲染 期望值:用户继续应用程序,单击方块以更改其值(通过单击方块画一个十字),板下的文本显示“恭喜!”,因为它基于“isComplete”的值 问题:如上所述,但“继续尝试”仍然存在 我添加了一个按钮来

我正在尝试使用React to learn UseContext和UseReducer制作一个简单的“Nonogram”/“Picross”游戏,但我不明白为什么我的顶级组件(应用程序)在使用的值发生变化时没有重新渲染。也许我遗漏了一些基本的东西,但我已经在网上阅读了文档和示例,不明白为什么不重新渲染

期望值:用户继续应用程序,单击方块以更改其值(通过单击方块画一个十字),板下的文本显示“恭喜!”,因为它基于“isComplete”的值

问题:如上所述,但“继续尝试”仍然存在

我添加了一个按钮来查看UseReducer函数中定义的boardState

代码如下:

App.js

import './App.css';
import { useReducer } from 'react';
import Table from './Table';
import BoardContext from './BoardContext';
import boardReducer from './BoardReducer';

function App() {
  //Puzzle layout
  const puzzleArray = [
    [true, false, true], 
    [false, true, false], 
    [true, false, true]
  ];

  //Creating a set of blank arrays to start the game as the userSelection
  const generateUserSelection = () => {
    const userSelection = [];
    puzzleArray.forEach(row => {
      let blankRow = [];
      row.forEach(square => {
        blankRow.push(false)
      });
      userSelection.push(blankRow);
    })
    return userSelection;
  };

  //Initial Context value
  const boardInfo = {
    puzzleName: "My Puzzle",
    puzzleArray: puzzleArray,
    userSelection: generateUserSelection(),
    isComplete: false
  };

  const [ boardState, dispatch ] = useReducer(boardReducer, boardInfo)

  return (
    <BoardContext.Provider value={{board: boardState, dispatch}}>
      <div className="App">
        <header className="App-header">
          <p>
            Picross
          </p>
          <Table />
        </header>
        <div>
          {boardState.isComplete ? 
            <div>Congratulations!</div>
          : <div>Keep trying</div>
          }
        </div>
        <button onClick={() => console.log(boardState)}>boardState</button>
      </div>
    </BoardContext.Provider>
  );
}

export default App;
(使用JSON.stringify作为检查匹配数组的廉价方法,因为它目前只是一个小问题!)

问题 您正在几个地方修改状态对象:

const handleToggle = () => {
  console.log(board);
  board.userSelection[position.row][position.column] = !board.userSelection[position.row][position.column]; // <-- mutation!
  dispatch(board);
  setIsSelected(!isSelected);
}
创建一个action creator,它实际上只是一个返回action对象的函数

const togglePosition = position => ({
  type: "TOGGLE",
  position
});
然后,
handleToggle
应该使用/传递已调度操作中的行和列位置

const handleToggle = () => dispatch(togglePosition(position));
简单演示

演示代码:

const puzzleArray = [
  [true, false, true],
  [false, true, false],
  [true, false, true]
];

const userSelection = Array(3).fill(Array(3).fill(false));

const togglePosition = (row, column) => ({
  type: "TOGGLE",
  position: { row, column }
});

const boardReducer = (state, action) => {
  if (action.type === "TOGGLE") {
    const { position } = action;
    const nextState = {
      ...state,
      userSelection: state.userSelection.map((rowEl, row) =>
        row === position.row
          ? rowEl.map((colEl, col) =>
              col === position.column ? !colEl : colEl
            )
          : rowEl
      )
    };

    nextState.isComplete =
      JSON.stringify(nextState.userSelection) ===
      JSON.stringify(state.puzzleArray);

    return nextState;
  }
  return state;
};

export default function App() {
  const [boardState, dispatch] = React.useReducer(boardReducer, {
    puzzleArray,
    userSelection,
    isComplete: false
  });

  const handleClick = (row, column) => () =>
    dispatch(togglePosition(row, column));

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <div>{boardState.isComplete ? "Congratulations!" : "Keep Trying"}</div>

      <div>
        {boardState.userSelection.map((row, r) => (
          <div key={r}>
            {row.map((col, c) => (
              <span
                key={c}
                className={classnames("square", { active: col })}
                onClick={handleClick(r, c)}
              />
            ))}
          </div>
        ))}
      </div>
    </div>
  );
}
常量数组=[
[对,错,对],
[假,真,假],
[对,错,对]
];
constuserselection=Array(3.fill)(Array(3.fill)(false));
const togglePosition=(行、列)=>({
键入:“切换”,
位置:{行,列}
});
const boardReducer=(状态、操作)=>{
如果(action.type==“切换”){
常数{position}=动作;
常数nextState={
……国家,
userSelection:state.userSelection.map((行,行)=>
行===position.row
?rowEl.map((colEl,col)=>
col==position.column?!colEl:colEl
)
:罗威尔
)
};
nextState.isComplete=
stringify(nextState.userSelection)===
stringify(state.puzzleArray);
返回下一状态;
}
返回状态;
};
导出默认函数App(){
const[boardState,dispatch]=React.useReducer(boardReducer{
拼图数组,
用户选择,
isComplete:错误
});
常量handleClick=(行、列)=>()=>
调度(切换位置(行、列));
返回(
你好,代码沙盒
开始编辑,看看神奇的发生!
{boardState.isComplete?“恭喜!”:“继续尝试”}
{boardState.userSelection.map((行,r)=>(
{row.map((col,c)=>(
))}
))}
);
}

看起来您正在改变您的状态。另外,
dispatch
应该使用动作创建者,而不是您的变异状态对象。您是否可以更新您的问题以包括您的动作创建者(如果有)和board reducer功能?已更新以包括my baordReducer。从你的评论中,我猜我对如何使用减速机函数的理解是错误的,因为我没有使用任何“动作创建者”?
const boardReducer = (state, updateInfo) => {
  let isComplete = false;

  if (JSON.stringify(updateInfo.userSelection) === JSON.stringify(state.puzzleArray)) {
    isComplete = true;
  }

  updateInfo.isComplete = isComplete; // <-- mutation!
  return updateInfo; // <-- returning mutated state object
}
const boardReducer = (state, action) => {
  if (action.type === "TOGGLE") {
    const { position } = action;
    const nextState = {
      ...state,
      userSelection: state.userSelection.map((rowEl, row) =>
        row === position.row
          ? rowEl.map((colEl, col) =>
              col === position.column ? !colEl : colEl
            )
          : rowEl
      )
    };

    nextState.isComplete =
      JSON.stringify(nextState.userSelection) ===
      JSON.stringify(state.puzzleArray);

    return nextState;
  }
  return state;
};
const togglePosition = position => ({
  type: "TOGGLE",
  position
});
const handleToggle = () => dispatch(togglePosition(position));
const puzzleArray = [
  [true, false, true],
  [false, true, false],
  [true, false, true]
];

const userSelection = Array(3).fill(Array(3).fill(false));

const togglePosition = (row, column) => ({
  type: "TOGGLE",
  position: { row, column }
});

const boardReducer = (state, action) => {
  if (action.type === "TOGGLE") {
    const { position } = action;
    const nextState = {
      ...state,
      userSelection: state.userSelection.map((rowEl, row) =>
        row === position.row
          ? rowEl.map((colEl, col) =>
              col === position.column ? !colEl : colEl
            )
          : rowEl
      )
    };

    nextState.isComplete =
      JSON.stringify(nextState.userSelection) ===
      JSON.stringify(state.puzzleArray);

    return nextState;
  }
  return state;
};

export default function App() {
  const [boardState, dispatch] = React.useReducer(boardReducer, {
    puzzleArray,
    userSelection,
    isComplete: false
  });

  const handleClick = (row, column) => () =>
    dispatch(togglePosition(row, column));

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <div>{boardState.isComplete ? "Congratulations!" : "Keep Trying"}</div>

      <div>
        {boardState.userSelection.map((row, r) => (
          <div key={r}>
            {row.map((col, c) => (
              <span
                key={c}
                className={classnames("square", { active: col })}
                onClick={handleClick(r, c)}
              />
            ))}
          </div>
        ))}
      </div>
    </div>
  );
}