일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 리액트기초
- nodejs교과서
- node
- express-generator
- 리액트컴포넌트
- 리액트
- component
- nodeJS
- sementicversion
- 시퀄라이즈공부
- 제로초예제
- 리액트스타디
- mongo
- NPM
- sequelize
- NoSQL
- React
- 클래스컴포넌트
- mongoose
- MongoDB
- React Component
- npm명령어
- 시퀄라이즈
- Today
- Total
개발노트
13. (3) useReducer 본문
추가할 기능
-한번 클릭했던 칸 다시 클릭 못하게,
-승자 판단, 무승부 판단
한번 클릭했던 칸 다시 클릭 못하게 하기
클릭하면 ''에서 'o'로 data 들어가게 되니까
const onClickTd = useCallback(() =>{
if( tdData ){
return;
}
...
,[tdData]);
(1)클릭했을때 tdData 값 있으면 그냥 리턴 시켜버리면 된다.
useCallback으로 감싸서 함수를 기억하고 있는데, 함수말고도; 너무잘 기억해서 다른 값들을 업데이트 안하니까,
바뀌는값이있을경우 꼭 의존성 배열에 추가해주어야 한다.
(2)tdData 바뀌는 값이니까 의존성배열에 추가해줌
dispatch 에서 state바뀔때 비동기 <-> 리덕스는 동기
dispatch({type: CHANGE_TURN}); //o 에서 x로 바꿔줘!
// turn값은 o로 나온다 비동기이기때문에
비동기에 따라서 처리할때는 uesEffect를 써야한다..
클릭할때마다
useEffect(()=>{ /*할일*/ } , [ something ])
의존성 배열에 tableData를 넣고, 그럼 tableData가바뀔때-(클릭할때마다) 마다 useEffect가 실행되니깐.
그때마다 할일을 적어준다.
승자 판단
효율적으로 체크하기 위해 클릭한 값에 해당되는 가로 세로 대각선만 검사한다.
그럴려면 클릭한 값을 저장해야 되서,
클릭 이벤트 와 함께 CLICK_TD액션이 일어나고 그걸 처리하는 reducer에서
case CLICK_TD : {
const tableData = [...state.tableData ];
tableData[action.tr] = [...tableData[action.tr]];
tableData[action.tr][action.td] = state.turn;
return{
...state,
tableData: tableData,
recentTd : [ action.tr, action.td] ////
}
}
const initialState = {
recentTd:[-1,-1]
}
이렇게 추가해줌.
그리고 판단해주는 useEffect
useEffect( ()=>{
const [ tr, td ] = recentTd;
if( tr < 0 ){
return;
}
//가로줄검사
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;
}
//대각선1
if (tableData[0][0] === turn && tableData[1][1] === turn && tableData[2][2] === turn) {
win = true;
}
//대각선2
if (tableData[0][2] === turn && tableData[1][1] === turn && tableData[2][0] === turn) {
win = true;
}
if(win){
dispatch({ type:SET_WINNER, winner: turn});
}else{
}
},[recentTd]);
Tr컴포넌트에서
dispatch({type:CLICK_TD, tr: trIndex, td: tdIndex }); //칸클릭, (o,x)의 표시
dispatch({type: CHANGE_TURN}); //o면x, x면o로 변경.
비동기문제의 발생.
현재클릭한애의 빙고체크 &현재trun인 사람이 WIN으로 useEffect 로직이 처리되있으나
클릭한애의 빙고체크 하고 있을때 다음턴으로 넘어가짐
그래서.. 코드변경 하면 된다
이긴거 아닐때, 다음턴으로 넘기기
if(win){
dispatch({ type:SET_WINNER, winner: turn});
}else{
dispatch({type: CHANGE_TURN}); //o면x, x면o로 변경.
}
무승부 검사
테이블에 데이터가 다 들어 있는지 검사하면 된다.
if(win){
dispatch({ type:SET_WINNER, winner: turn});
}else{
let all = true; //무승부가true인상태
//무승부검사
tableData.forEach( (tr)=>{
row.forEach( (td)=>{
if(!td){ //td하나라도 안차있으면 무승부false
all=true;
}
})
});
if(all){
}else{
dispatch({type: CHANGE_TURN}); //o면x, x면o로 변경.
}
}
(결판났으니) 게임 리셋
case RESET_GAME :{
return{
...state,
turn :'o',
tableData: [ ['','',''],['','',''],['','',''] ],
recentTd:[-1,-1]
}
}
reducer로 게임리셋액션 처리를 만들었고,
승리일때, 무승부일때 -게임리셋하도록 액션실행 하면된다.
성능최적화
리셋과, 승리표시도 잘해주지만
빨리클릭할경우 렌더링 계속 일어나서
성능최적화를 해야 된다
(한칸만 클릭했는데 전체가 렌더링 되서 문제)
import React ,{ useCallback } from 'react';
import { CLICK_TD } from './Tictactoe';
const Td = ({trIndex, tdIndex, dispatch ,tdData})=>{
console.log('td렌더링'); ///
const onClickTd = useCallback(() =>{
console.log( trIndex, tdIndex);
if( tdData ){
return;
}
dispatch({type:CLICK_TD, tr: trIndex, td: tdIndex }); //칸클릭, (o,x)의 표시
},[tdData]);
return(
<td onClick={onClickTd} >{tdData}</td>
)
};
export default Td;
지금.. 9번 렌더링 이러나는것도 모자라 9*2로 일어나고 있음.
문제를 파악하기 위해
const Td = ({ propsA })=>{
const ref = useRef([]);
useEffect( ()=>{
console.log( propsA === ref.current[0])
ref.current = [ propsA/* props */]
},[ propsA/* props들 */])
...
}
이런식으로 useRef를 사용해
(A) propsA 의값을 저장하고 => ref.current[0] 여기에저장됨
만약 propsA의 값이 바뀌면
A에서 저장했던 이전값ref.current[0]과 현재값propsA인 가 false가 나오게 됨.
const Td = ({trIndex, tdIndex, dispatch ,tdData})=>{
const ref = useRef([]);
useEffect( ()=>{
console.log( trIndex === ref.current[0], tdIndex === ref.current[1],
dispatch === ref.current[2], tdData === ref.current[3]);
ref.current = [ trIndex, tdIndex, dispatch ,tdData]
},[ trIndex, tdIndex, dispatch ,tdData ])
}
이런식으로 props로 받은 값들중 어떤 값이 바뀌어서 리렌더링 되고있는건지 알아내도록 한다
지금 Td컴포넌트여기서는, tdData가 바뀌어서(''->O) 리렌더링 되고 있는것이고, (원하는값만 잘바뀌고 있는상태)
그래서 부모컴포넌트Tr컴포넌트에서 props를 넘겨줄때(넘겨서 값을변경할때)
반복문을 써서 자식컴포넌트가 전부다 리렌더링 되고 있는 문제 ..가되는듯 하여
Tr컴포넌트의 자식인 Td컴포넌트에 React.memo를 써준다
이렇게 td렌더링이 한번만 일어나게 된다.
마찬가지로, Table컴포넌트에서 반복문으로 생성하는 Tr컴포넌트도 렌더링이 필요없이많이 발생하는걸 확인할 수 있는데, 똑같이 memo컴포넌트로 감싸준다.
import React , { useState , useReducer , useCallback, useEffect } from 'react';
import Table from './Table';
const initialState = {
winner: '',
turn :'o',
tableData: [ ['','',''],['','',''],['','',''] ],
recentTd:[-1,-1]
}
export const SET_WINNER = 'SET_WINNER';
export const CLICK_TD = 'CLICK_TD';
export const CHANGE_TURN = 'CHANGE_TURN';
export const RESET_GAME = 'RESET_GAME';
const reducer = (state, action) =>{
switch( action.type ){
case SET_WINNER :
return{
...state,
winner:action.winner
}
case CLICK_TD : {
const tableData = [...state.tableData ];
tableData[action.tr] = [...tableData[action.tr]];
tableData[action.tr][action.td] = state.turn;
return{
...state,
tableData: tableData,
recentTd : [ action.tr, action.td]
}
}
case CHANGE_TURN : {
return{
...state,
turn: state.turn === 'o'?'x':'o'
}
}
case RESET_GAME :{
return{
...state,
turn :'o',
tableData: [ ['','',''],['','',''],['','',''] ],
recentTd:[-1,-1]
}
}
default:
return state;
}
}
const Tictactoe = () =>{
const [state, dispatch] = useReducer( reducer, initialState );
const { recentTd, tableData, winner, turn } = state;
const onClickTable = useCallback ( ()=>{
},[]);
useEffect( ()=>{
const [ tr, td ] = recentTd;
if( tr < 0 ){
return;
}
let win = false;
if (tableData[tr][0] === turn && tableData[tr][1] === turn && tableData[tr][2] === turn) {
win = true;
}
if (tableData[0][td] === turn && tableData[1][td] === turn && tableData[2][td] === 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, winner: turn });
}else{
let all = true; //무승부가true
tableData.forEach( (tr)=>{
tr.forEach( (td)=>{
if(!td){
all=false;
}
})
});
if(all){
dispatch({type:RESET_GAME, winner: turn });
}else{
dispatch({type: CHANGE_TURN});
}
}
},[recentTd]);
return (
<>
<Table onClickTableEvent={onClickTable}
tableData={tableData}
dispatch={dispatch}
/>
{winner && <p>{winner} 님의 승리</p> }
</>
)
}
export default Tictactoe;
import React from 'react';
import Tr from './Tr';
const Table = ( {onClickTableEvent, tableData ,dispatch} )=>{
return(
<>
<table >
<tbody>
{ Array(tableData.length).fill().map( ( tr ,i )=>( <Tr trIndex={i} trData={ tableData[i] } dispatch={dispatch}/> ) ) }
</tbody>
</table></>
)
}
export default Table;
import React ,{ memo} from 'react';
import Td from './Td';
const Tr = memo(({trData ,trIndex, dispatch}) =>{
console.log('tr렌더링');
return(
<tr>
{ Array(trData.length).fill().map(( td ,i )=>(
<Td trIndex={trIndex} tdIndex={i}
dispatch={dispatch}
tdData = {trData[i]}
/> ) )}
</tr>
)
});
export default Tr;
import React ,{ useCallback, useEffect, useRef ,memo} from 'react';
import { CLICK_TD } from './Tictactoe';
const Td = memo( ({trIndex, tdIndex, dispatch ,tdData})=>{
//console.log('td렌더링');
const ref = useRef([]);
useEffect( ()=>{
//console.log( trIndex === ref.current[0], tdIndex === ref.current[1],
// dispatch === ref.current[2], tdData === ref.current[3]);
//console.log( "ref.current[3]:"+ref.current[3] );
//console.log( "tdData:"+tdData );
ref.current = [ trIndex, tdIndex, dispatch ,tdData]
},[ trIndex, tdIndex, dispatch ,tdData ])
const onClickTd = useCallback(() =>{
console.log( trIndex, tdIndex);
if( tdData ){
return;
}
dispatch({type:CLICK_TD, tr: trIndex, td: tdIndex }); //칸클릭, (o,x)의 표시
},[tdData]);
return(
<td onClick={onClickTd} >{tdData}</td>
)
});
export default Td;
'React > basic' 카테고리의 다른 글
14 (2)context API (0) | 2020.07.29 |
---|---|
14. context API (0) | 2020.07.29 |
13. (2) useReducer (0) | 2020.07.29 |
13. useReducer (0) | 2020.07.28 |
12. hooks / useEffect , useMemo (0) | 2020.07.28 |