TIL 22-04-20
React.js
Today I Learned ... react.js
🙋♂️ React.js Lecture
🙋 My Dev Blog
React Lecture CH 7
1 - useReducer
2 -reducer, action, dispatch
3-action dispatchの作成
4-Tick Taxtoゲーム
5-テーブル最適化
🙋♂️ 解決すべき問題
1.クリックしない場所を再度クリック
2.勝者を決めるロジックの実現(勝者)
+引き分けの場合
タッチダウンをクリア
1.一度押さないユニット
Td.jsx
const Td = ({ rowIndex, cellIndex, dispatch, cellData }) => {
const onClickTd = useCallback(() => {
if (cellData) {
return; // 셀 데이터가 존재하면 빠져나옴 -> 한번 클릭한 셀은 변경되지 않게
}
dispatch({ type: CLICK_CELL, row: rowIndex, cell: cellIndex });
dispatch({ type: CHANGE_TURN });
}, [cellData]);
return <td onClick={onClickTd}>{cellData}</td>;
};
->breakを使用してonClicktd関数を終了します.
注意2-stateは非同期です!
cf)Reduxは同期している
->非同期状態を処理するには、
useEffect
を使用する必要があります.2.勝者(勝者)判断ロジック
OかXが勝つ場合は大体4種類あります.
let win = false;
if (
tableData[row][0] === turn &&
tableData[row][1] == turn &&
tableData[row][2] === turn
) {
win = true;
}
if (
tableData[0][cell] === turn &&
tableData[1][cell] === turn &&
tableData[2][cell] === turn
) {
win = true;
}
if (
tableData[0][0] === turn &&
tableData[1][1] === turn &&
tableData[2][2] === turn
) {
win = true;
}
if (
tableData[0][2] === turn &&
tableData[1][1] === turn &&
tableData[2][0] === turn
) {
win = true;
}
tableDataは非同期で変更されているため、usStateを使用する必要があります.
TicTacToe.jsx
const reducer = (state, action) => {
switch (action.type) {
// 생략
case CLICK_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...tableData[action.row]];
tableData[action.row][action.cell] = state.turn;
return {
...state,
tableData,
recentCell: [action.row, action.cell], // 👈 추가
};
現在のセルの位置を表すstate(recentCell
)を追加します.CLICK CELLをreduceで実行するたびに、現在のセルの位置が保存されます.
const initialState = {
winner: '',
turn: 'O',
tableData: [
['', '', ''],
['', '', ''],
['', '', ''],
],
recentCell: [-1, -1], // 👈 추가
};
recentcellを(初期値は-1、すなわちインデックスなし)
// useEffect
useEffect(() => {}, [recentCell]);
useEffect(() => {
const [row, cell] = recentCell;
if (row < 0) {
return;
// useEffect는 첫 렌더링시에도 실행되는데, 이를 막기 위함.
}
let win = false;
if (
tableData[row][0] === turn &&
tableData[row][1] == turn &&
tableData[row][2] === turn
) {
win = true;
}
if (
tableData[0][cell] === turn &&
tableData[1][cell] === turn &&
tableData[2][cell] === turn
) {
win = true;
}
if (
tableData[0][0] === turn &&
tableData[1][1] === turn &&
tableData[2][2] === turn
) {
win = true;
}
if (
tableData[0][2] === turn &&
tableData[1][1] === turn &&
tableData[2][0] === turn
) {
win = true;
}
if (win) {
dispatch({ type: SET_WINNER, winner: turn });
} else {
// 무승부 검사
}
}, [recentCell]);
useEffectの変更
運転しないようにドアを閉めます.
条件はif(row<0)です.
-> dispatch({ type: SET_WINNER, winner: turn });
->1)引き分けチェック(テーブルがいっぱいかどうか)
->2)次のラウンド(OからXまで)
let all = true;
tableData.forEach((row) => {
row.forEach((cell) => {
if (!cell) {
all = false;
}
});
});
チャットルームは空いていますか?
1-2)引き分けの場合=つまりテーブルがいっぱい(all=true)
if (all) {
// 무승부면 리셋
dispatch({ type: SET_WINNER, winner: null });
dispatch({ type: RESET_GAME });
}
2)引き分けでなければ次のラウンドに移るelse {
// 무승부 아니면 턴 넘김
dispatch({ type: CHANGE_TURN });
}
🔻 useEffectフルコード
useEffect(() => {
const [row, cell] = recentCell;
if (row < 0) {
return;
// useEffect는 첫 렌더링시에도 실행되는데, 이를 막기 위함.
}
let win = false;
if (
tableData[row][0] === turn &&
tableData[row][1] == turn &&
tableData[row][2] === turn
) {
win = true;
}
if (
tableData[0][cell] === turn &&
tableData[1][cell] === turn &&
tableData[2][cell] === turn
) {
win = true;
}
if (
tableData[0][0] === turn &&
tableData[1][1] === turn &&
tableData[2][2] === turn
) {
win = true;
}
if (
tableData[0][2] === turn &&
tableData[1][1] === turn &&
tableData[2][0] === turn
) {
win = true;
}
if (win) {
// 승리시
dispatch({ type: SET_WINNER, winner: turn });
dispatch({ type: RESET_GAME });
} else {
// 무승부 검사 - 칸이 다 차있으면 (즉, all이 true면) 무승부임
let all = true;
tableData.forEach((row) => {
row.forEach((cell) => {
if (!cell) {
all = false;
}
});
});
if (all) {
// 무승부면 리셋
dispatch({ type: SET_WINNER, winner: null });
dispatch({ type: RESET_GAME });
} else {
// 무승부 아니면 턴 넘김
dispatch({ type: CHANGE_TURN });
}
}
}, [recentCell]);
<結果>
パフォーマンスの最適化
Chrome Dev Toolを利用
レンダーセクションを表示します.
明確なセルをクリックして変更するだけで、1つのセルを変更するたびにセル全体がレンダリングされます.
->useEffectとuseRefを使用してパフォーマンスを最適化します.
useRef, useEffect
const Td = ({ rowIndex, cellIndex, dispatch, cellData }) => {
const ref = useRef([]);
useEffect(() => {
console.log(
rowIndex === ref.current[0],
cellIndex === ref.current[1],
dispatch === ref.current[2],
cellData === ref.current[3]
);
ref.current = [rowIndex, cellIndex, dispatch, cellData];
}, [rowIndex, cellIndex, dispatch, cellData]);
// 모든 props를 다 적어줌
...
}
だからこそ、レンダリングが発生します.
(useref+userEffectの比較)
->
cellData
が再レンダリングに置き換えられています.console.ロゴを撮ったところ、td自体が自分の欲しいものだけを変えていることに気づきました.
この時は反応してmemoでPureComponentに変えればいいです
React.memoの使用
import React, { useCallback, memo } from 'react';
import { CLICK_CELL } from './TicTacToe';
const Td = memo(({ rowIndex, cellIndex, dispatch, cellData }) => {
console.log('Td render');
const onClickTd = useCallback(() => {
if (cellData) {
return; // 셀 데이터가 존재하면 빠져나옴 -> 한번 클릭한 셀은 변경되지 않게
}
dispatch({ type: CLICK_CELL, row: rowIndex, cell: cellIndex });
}, [cellData]);
return <td onClick={onClickTd}>{cellData}</td>;
});
export default Td;
Tr.jsximport React, { memo } from 'react';
import Td from './Td';
const Tr = memo(({ rowData, rowIndex, dispatch }) => {
console.log('Tr render');
return (
<tr>
{Array(rowData.length)
.fill()
.map((td, i) => (
<Td
key={i}
rowIndex={rowIndex}
cellIndex={i}
cellData={rowData[i]}
dispatch={dispatch}
>
{''}
</Td>
))}
</tr>
);
});
export default Tr;
tdをクリックすると
(子をレンダリングするときに親がレンダリングされるように見える)
+) React.userMemoを使用する場合
import React, { useMemo } from 'react';
import Td from './Td';
const Tr = ({ rowData, rowIndex, dispatch }) => {
console.log('Tr render');
return (
<tr>
{Array(rowData.length)
.fill()
.map((td, i) =>
useMemo(
() => (
<Td
key={i} rowIndex={rowIndex} cellIndex={i} cellData={rowData[i]} dispatch={dispatch}
>
{''}
</Td>
),
[rowData[i]]
)
)}
</tr>
);
};
export default Tr;
(関数の値を記憶)
最後の手段としての反応.useMemo()を使用します.
->構成部品自体を覚えます.
Reference
この問題について(TIL 22-04-20), 我々は、より多くの情報をここで見つけました https://velog.io/@thisisyjin/TIL-22-04-19-2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol