22-04-20 (3)
React.js
Today I Learned ... react.js
🙋♂️ React.js Lecture
🙋 My Dev Blog
지난 시간 리뷰
-
React.createContext
export하여 하위 컴포넌트에서도 사용 가능하게.
-> ()안에 초기값을 작성해준다. -
useContext
import한 context명을 작성해준다.
- JSX내에서
<Provider value={value}>
-> value는 useMemo로 캐싱해줘야 성능 최적화 가능.
React Lecture CH 8
1 - context API
2 - createContext와 Provider
3 - useContext
4 - 좌클릭, 우클릭 로직
5 - 지뢰 개수 표시
6 - 빈칸 한번에 열기
7 - 승리 조건 체크, 타이머
8 - context API 최적화
좌클릭, 우클릭 로직
1) 좌클릭 - OPEN_CELL
MineSearch.jsx
const reducer = (state, action) => {
switch (action.type) {
case START_GAME:
return {
...state,
tableData: plantMine(action.row, action.cell, action.mine),
};
// 🔻 추가
case OPEN_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...state.tableData[action.row]];
tableData[action.row][action.cell] = CODE.OPENED;
return {
...state,
tableData,
};
}
default:
return state;
}
};
- 얕은 복사를 한 후([...]), 해당 셀에 CODE.OPENED를 할당하면 된다.
Td.jsx
const Td = ({ rowIndex, cellIndex }) => {
const { tableData, dispatch } = useContext(TableContext);
const onClickTd = useCallback(() => {
dispatch({ type: OPEN_CELL, row: rowIndex, cell: cellIndex });
}, []);
return (
<td style={getTdStyle(tableData[rowIndex][cellIndex])} onClick={onClickTd}>
{getTdText(tableData[rowIndex][cellIndex])}
</td>
);
};
- Td를 클릭했을때 발생하는
onClickTd
함수를 작성함. - JSX 어트리뷰트에 직접 넣는 함수는 ->
useCallback
사용!
- dispatch를 useContext로 받아온 후, action 객체를 넣어줌.
-> row, cell 정보는 각각 (props로 받은) rowIndex, cellIndex를 대입.
<결과>
Td를 클릭시 하얀색으로 변함.
즉, tableData[rowIndex][cellIndex]에 CODE.OPENED
가 대입되어, getTdStyle에 의해 background가 #fff
가 되는 것.
해결할 점
- 지뢰가 있는 부분도 하얗게 됨
- 숫자가 나와야 함
onClickTd 세분화
tableData[rowIndex][cellIndex]
의 값에 따라서 동작이 달라져야함.
-> switch문을 이용.
Td.jsx
const onClickTd = useCallback(() => {
switch (tableData[rowIndex][cellIndex]) {
case CODE.OPENED:
case CODE.FLAG_MINE:
case CODE.FLAG:
case CODE.QUESTION_MINE:
case CODE.QUESTION:
return;
case CODE.NORMAL:
dispatch({ type: OPEN_CELL, row: rowIndex, cell: cellIndex });
return;
case CODE.MINE:
dispatch({ type: CLICK_MINE, row: rowIndex, cell: cellIndex });
return;
}
}, [tableData[rowIndex][cellIndex]]);
- OPENED, FLAG, QUESTION일때는 클릭해도 변화가 없게 함. (return;으로 빠져나감)
- NORMAL 일때는 OPEN_CELL 액션이 실행되도록.
(상태는 OPENED가 되고, 스타일도 적용) - MINE을 클릭했을때는 CLICK_MINE 액션이 실행되도록. (지뢰가 터지는 액션. 추후 작성함)
- useCallback 두번째 인자는 그 값이 바뀔때마다 함수도 다시 저장되도록 함.
스타일 + 텍스트 세분화
Td 컴포넌트의 getTdStyle, getTdText를 수정해보자.
const getTdStyle = (code) => {
switch (code) {
case CODE.NORMAL:
case CODE.MINE:
return {
background: '#444',
};
case CODE.CLICKED_MINE:
case CODE.OPENED:
return {
background: '#fff',
};
case CODE.QUESTION:
case CODE.QUESTION_MINE:
return {
background: 'yellow',
};
case CODE.FLAG:
case CODE.FLAG_MINE:
return {
background: 'red',
};
default:
return {
background: '#fff',
};
}
};
const getTdText = (code) => {
switch (code) {
case CODE.NORMAL:
return '';
case CODE.MINE:
return 'X';
case CODE.CLICKED_MINE:
return '펑';
case CODE.FLAG:
case CODE.FLAG_MINE:
return '!';
case CODE.QUESTION:
case CODE.QUESTION_MINE:
return '?';
default:
return '';
}
};
2) 우클릭 - onContextMenu
Td.jsx
const onRightClickTd = useCallback(
(e) => {
e.preventDefault();
switch (tableData[rowIndex][cellIndex]) {
case CODE.NORMAL:
case CODE.MINE:
dispatch({ type: FLAG_CELL, row: rowIndex, cell: cellIndex });
return;
case CODE.FLAG:
case CODE.FLAG_MINE:
dispatch({ type: QUESTION_CELL, row: rowIndex, cell: cellIndex });
return;
case CODE.QUESTION:
case CODE.QUESTION_MINE:
dispatch({ type: NORMALIZE_CELL, row: rowIndex, cell: cellIndex });
return;
default:
return;
}
},
[tableData[rowIndex][cellIndex]]
);
// JSX
return (
<td
style={getTdStyle(tableData[rowIndex][cellIndex])}
onClick={onClickTd}
onContextMenu={onRightClickTd}
>
{getTdText(tableData[rowIndex][cellIndex])}
</td>
);
- 우클릭시 기본 동작을 막기위해 (컨텍스트메뉴가 뜨는 것) e.preventDefault를 해준다.
- onRightClickTd 함수도
tableData[rowIndex][cellIndex]
의 값에 의해 동작이 달라져야 하므로, switch문 작성.
MineSearch.jsx
...
export const START_GAME = 'START_GAME';
export const OPEN_CELL = 'OPEN_CELL';
export const CLICK_MINE = 'CLICK_MINE';
export const FLAG_CELL = 'FLAG_CELL';
export const QUESTION_CELL = 'QUESTION_CELL';
export const NORMALIZE_CELL = 'NORMALIZE_CELL';
- 우선 export로 모듈로 만들고, Td 에서 전부 import함.
추가 state -
halted
- 지뢰가 터졌을때 게임을 일시정지하는 state.
const initialState = { tableData: [], timer: 0, result: '', halted: false, // 👈 추가 };
- 이제, CLICK_CELL 액션을 처리하는 reducer을 작성해보면
case CLICK_MINE: {
const tableData = [...state.tableData];
tableData[action.row] = [...state.tableData[action.row]];
tableData[action.row][action.cell] = CODE.CLICKED_MINE;
return {
...state,
tableData,
halted: true,
};
}
-
halted
를 true로 하여 게임이 중단되게 한다. -
단, START_GAME 액션에 return 부분에 halted: false를 추가하여 게임을 다시 시작할 수 있도록.
case START_GAME:
return {
...state,
tableData: plantMine(action.row, action.cell, action.mine),
halted: false,
};
halted를 initialState와 creactContext에도 추가해주고,
Td 컴포넌트에서 useContext()로 불러와준다.
MineSearch.jsx
export const TableContext = createContext({
tableData: [],
halted: true,
dispatch: () => {},
});
const initialState = {
tableData: [],
timer: 0,
result: '',
halted: true,
};
- 초기값은 true로 하고, 처음에 버튼 클릭시 false가 되게 한다.
Td.jsx
const { tableData, dispatch, halted } = useContext(TableContext);
- 초기값은 true
- 시작 버튼 클릭시 false가 됨
- if(halted) return; 으로 필터링 함.
-> onClickTd, onRightClickTd에서.
->halter
가 true면 셀 클릭 못하게 (게임 중단)
const onClickTd = useCallback(() => {
if (halted) {
return;
}
...
}
const onRightClickTd = useCallback(
(e) => {
e.preventDefault();
if (halted) {
return;
}
...
}
나머지 reducer 작성
- FLAG_CELL, QUESTION_CELL, NORMALIZE_CELL에 대한 reducer도 작성해줌.
MineSearch.jsx
case FLAG_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...state.tableData[action.row]];
if (tableData[action.row][action.cell] === CODE.MINE) {
tableData[action.row][action.cell] = CODE.FLAG_MINE;
} else {
tableData[action.row][action.cell] = CODE.FLAG;
}
return {
...state,
tableData,
};
}
case QUESTION_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...state.tableData[action.row]];
if (tableData[action.row][action.cell] === CODE.FLAG_MINE) {
tableData[action.row][action.cell] = CODE.QUESTION_MINE;
} else {
tableData[action.row][action.cell] = CODE.QUESTION;
}
return {
...state,
tableData,
};
}
case NORMALIZE_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...state.tableData[action.row]];
if (tableData[action.row][action.cell] === CODE.QUESTION_MINE) {
tableData[action.row][action.cell] = CODE.MINE;
} else {
tableData[action.row][action.cell] = CODE.NORMAL;
}
return {
...state,
tableData,
};
}
- 전부 원리는 같다.
- 우선, 원본이 변하면 안되므로, 얕은 복사를 통해 tableData를 복사한다.
- tableData[action.row]도 얕은 복사 한다.
(이차원 배열이므로 펼쳐줘야함)- if-else 로직으로 일반 셀인지 (NORMAL), 지뢰 셀인지 (MINE) 체크해서 구분해줌.
Result
지뢰 갯수 나타내기
-
빈 칸을 누르면 주변에 지뢰가 몇 개가 있는지 나타내야 함
-> OPEN_CELL의 reducer에 작성. -
빈칸만 여러개 있는 곳을 눌렀을 때는, 주변 칸까지 한번에 열리게
-> 재귀를 통해 구현.
주변 지뢰 개수 카운트
- OPEN_CELL 로직 구현.
- 위 or 아래칸이 존재하는가에 대한 차이로
검사해야할 주변 셀의 개수가 달라짐.
-> 만약
MineSearch.jsx
case OPEN_CELL: {
const tableData = [...state.tableData];
tableData[action.row] = [...state.tableData[action.row]];
tableData[action.row][action.cell] = CODE.OPENED;
let around = [];
if (tableData[action.row - 1]) {
around = around.concat(
tableData[action.row - 1][action.cell - 1],
tableData[action.row - 1][action.cell],
tableData[action.row - 1][action.cell + 1]
);
}
around = around.concat(
tableData[action.row][action.cell - 1],
tableData[action.row][action.cell + 1]
);
if (tableData[action.row + 1]) {
around = around.concat(
tableData[action.row + 1][action.cell - 1],
tableData[action.row + 1][action.cell],
tableData[action.row + 1][action.cell - 1]
);
}
const count = around.filter((v) =>
[CODE.MINE, CODE.FLAG_MINE, CODE.QUESTION_MINE].includes(v)
).length;
console.log(around, count);
tableData[action.row][action.cell] = count;
return {
...state,
tableData,
};
}
- concat은 배열과 배열을 합쳐줌.
(원본 변경은 x. 새 배열 반환) - 주변에 지뢰가 있는지 카운트하여 개수를 셈.
-> filter([].includes()) 를 해서 지뢰 셀을 저장하고, 이 배열의 length는 곧 지뢰의 개수임. Td.jsxconst getTdText = (code) => { switch (code) { ... default: return code || ''; }
- 기본적으로 code (=tableData[rowIndex][cellIndex])를 출력하고,
0이면(지뢰가없으면) 공백을 출력하게.
다음시간 - 빈칸 한번에 열리게 (재귀함수)
Author And Source
이 문제에 관하여(22-04-20 (3)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@thisisyjin/22-04-20-3저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)