UseContext 및 UseReducer를 배우기 위해 React를 사용하여 간단한 'Nonogram'/ 'Picross'게임을 만들려고하는데, 사용하는 값이 변경 될 때 내 상위 구성 요소 (App)가 다시 렌더링되지 않는 이유에 대해 의아해합니다. 아마도 나는 기본적인 것을 놓치고 있지만 온라인에서 문서와 예제를 읽었으며 왜 다시 렌더링되지 않는지 알 수 없습니다.
예상 : 사용자가 응용 프로그램으로 이동하여 사각형을 클릭하여 값을 변경하고 (사각형을 클릭하여 십자가를 그립니다) 보드 아래의 텍스트는 '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;
Table.jsx :
import { useContext, useEffect } from 'react';
import './App.css';
import Square from './Square';
import BoardContext from './BoardContext';
function Table() {
useEffect(() => {console.log('table useEffect')})
const { board } = useContext(BoardContext);
const generateTable = solution => {
const squareLayout = []
for (let i = 0; i < solution.length; i++) {
const squares = []
for (let j = 0; j < solution[i].length; j++) {
squares.push(
<Square
position={{row: i, column: j}}
/>
);
};
squareLayout.push(
<div className="table-row">
{squares}
</div>
);
};
return squareLayout;
};
return (
<div className="grid-container">
{generateTable(board.puzzleArray)}
</div>
);
}
export default Table;
Square.jsx
import { useContext, useState } from 'react';
import './App.css';
import BoardContext from './BoardContext';
function Square(props) {
const { board, dispatch } = useContext(BoardContext)
const [ isSelected, setIsSelected ] = useState(false);
const { position } = props;
const handleToggle = () => {
console.log(board)
board.userSelection[position.row][position.column] = !board.userSelection[position.row][position.column]
dispatch(board);
setIsSelected(!isSelected);
}
return (
<div className={`square ${isSelected ? " selected" : ""}`}
onClick={handleToggle}
>
{position.row}, {position.column}
</div>
);
}
export default Square;
감사
편집 : 나는 이와 같은 간단한 응용 프로그램의 경우 상태를 소품을 통해 전달하는 것이 매우 쉬울 것이라는 것을 알고 있지만 아이디어는 다른 후크를 연습하는 것이므로 피하고 싶습니다. 내가 여기서 실천하고있는 아이디어는 이상적으로는 미래의 더 큰 프로젝트로 확장 될 수있을 것입니다.
편집 2 : 요청에 따라 다음은 내 BoardReducer.js 파일입니다.
const boardReducer = (state, updateInfo) => {
let isComplete = false;
if (JSON.stringify(updateInfo.userSelection) === JSON.stringify(state.puzzleArray)) {
isComplete = true;
}
updateInfo.isComplete = isComplete;
return updateInfo;
}
export default boardReducer;
(JSON.stringify를 사용하여 일치하는 배열을 확인하는 저렴한 방법으로 지금은 작은 것입니다!)
몇 군데에서 상태 객체를 변경합니다.
const handleToggle = () => {
console.log(board);
board.userSelection[position.row][position.column] = !board.userSelection[position.row][position.column]; // <-- mutation!
dispatch(board);
setIsSelected(!isSelected);
}
그리고 감속기에서
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
}
새로운 상태 객체가 생성되지 않기 때문에 React는 상태 변경을 보지 않고 UI를 다시 렌더링하지 않습니다.
useReducer는 일반적으로 감속기 함수는 현재 소비에 "REDUX"패턴 사용됩니다 state
및 action
그 상태에서 작동하고, 반환 새로운 상태 개체를.
사용자 선택을 토글하고 전체 보드를 확인하는 작업을 발송해야합니다.
보드 감속기
상태를 업데이트 할 때 전체 state
개체 부터 시작하여 업데이트중인 모든 상태 개체를 새 개체 참조로 얕게 복사해야 합니다.
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
});
그런 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>
);
}
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다