일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- NPM
- NoSQL
- 리액트
- sementicversion
- 리액트기초
- nodejs교과서
- 제로초예제
- 리액트컴포넌트
- 클래스컴포넌트
- node
- mongoose
- npm명령어
- React Component
- sequelize
- express-generator
- 리액트스타디
- mongo
- React
- component
- 시퀄라이즈공부
- 시퀄라이즈
- MongoDB
- nodeJS
- Today
- Total
개발노트
14. context API 본문
useReducer, contextAPI 를 사용해서 지뢰찾기 게임 만들기
Mine Form Table Tr Td 컴포넌트
Mine컴포넌트
(1)useReducer 만들고
(2)초기값들 tableData선언
(3)reducer함수만들고,
import React ,{ useReducer, createContext }from 'react';
import Form from './Form';
const initialState = { //(2)
tableData:[],
timer:0,
result:0,
}
const reducer = (state, action)=>{ //(3)
switch (action.type){
default:
return state;
}
}
const Mine = () =>{
const [state, dispatch ] = useReducer( reducer,initialState); //(1)
const { timer , result } = state;
return(
<>
<h3>지뢰찾기</h3>
<Form />
<p>타이머:{timer}</p>
<p>결과:{result}</p>
</>
)
}
export default Mine;
Form컴포넌트
화면 몇 by 몇, 지뢰갯수, 타이머 값을 받을 Form컴포넌트를 만든다
(1)useState로 state이름, setState를만들고,
(2)react는input바뀔때 값 직접바꿔줘야 되니까 각각 onChange이벤트걸어서 e.target.value로 set해준다.
그리고 게임시작 버튼을 만든다
(여기 게임시작버튼에 context API적용예정)
import React ,{useState, useCallback} from 'react';
const Form = () =>{
const [ row, setRow ] = useState( 10); //(1)
const [ col, setCol] = useState(10);
const [ mine, setMine] = useState(20);
const onChangeRow = useCallback( (e) =>{ //(2)
setRow(e.target.value);
});
const onChangeCol = useCallback((e) =>{
setCol(e.target.value);
});
const onChangeMine = useCallback((e) =>{
setMine(e.target.value);
});
const onClickButton = () =>{
}
return(
<>
<input type="number" placeholder="가로칸 갯수" value={ row} onChange={onChangeRow} />
<input type="number" placeholder="세로칸 갯수" value={ col} onChange={onChangeCol} />
<input type="number" placeholder="지뢰 갯수" value={ mine} onChange={onChangeMine} />
<button onClick={onClickButton}>게임시작</button>
</>
);
}
export default Form;
context API - createContext (Mine component)
(1) React.createContext를 불러와서 createContext()함수호출해준다.
-초기값을 넣어줌
-이 context를 다른파일에 쓸수있게 export 해줌!
(2)JSX안에서 context 에 접근하고 싶은 컴포넌트들을 context.provider 로 묶어준다
(3) TblContextValue로 넘겨주고싶은 값들을 넣어줌
import React ,{ useEffect, useReducer, createContext }from 'react';
import Form from './Form';
//const TableContext = createContext(); //(1)
export const TableContext = createContext({ //(1)
tableData:[],
dispatch:()=>{}
});
...
const Mine = () =>{
const [state, dispatch ] = useReducer( reducer,initialState);
const { timer , result , tableData } = state;
return(
<TableContext.Provider TblContextValue={{ tableData: tableData, dispatch}} > <!-- (2)(3) -->
<h3>지뢰찾기</h3>
<Form />
<p>타이머:{timer}</p>
<p>결과:{result}</p>
</TableContext.Provider>
)
}
export default Mine;
context API - useContext (Form component)
React.useContext를 사용해서 export한 TableContext 를 받아온다
//Form.jsx
const tblContextValue = useContext(TableContext);
TableContext(Context)를 받아왔으니, TblContextValue 값으로 넣어준 dispatch에 접근이 가능
//Mine.jsx
<TableContext.Provider tblContextValue={{ tableData: tableData, dispatch}} >
</TableContext.Provider>
//Form.jsx
const tblContextValue = useContext(TableContext);
const dispatch = tblContextValue.dispatch; // 이렇게 dispatch에 접근이 가능
이걸 비구조화할당으로
const { dispatch } = useContext(TableContext);
그리고 TableContext.Provider 에서 전달하는 값들이
함수컴포넌트가 렌더링될때 매번 tblContextValue객체들이 생기지 않게 (만들지 않게)
useMemo를 써서 보낸다
const tblContextValue = useMemo( ()=>({ tableData, dispatch },[tableData]) );
return(
<TableContext.Provider value={tblContextValue} > //꼭 value로 넘겨줘야 함...
...
Form컴포넌트에서 액션을 dispatch시켜서 입력받은 칸갯수와 지뢰갯수를 보내고,
const onClickButton = useCallback( () =>{
dispatch({ type:START_GAME, row, col, mine });
}, [row, col, mine]);
Mine 컴포넌트에서 reducer로 action.row ... 로 값들을 얻어와 액션을 처리
export const START_GAME = 'START_GAME';
const reducer = ()=>{
switch (action.type){
case START_GAME:
return{
...state,
tableData: plantMine(action.row, action.col, action.mine)
};
...
}
2차원 배열을 지뢰 넣어서 만들기
칸마다 상태코드가 있음. 기본 -1 ,지뢰는 -7
export const TDstate = {
MINE:-7, //지뢰심어짐
NORMAL:-1, //기본
FLAG: -3, //우클릭1 깃발
QUESTION:-2, //우클릭2 물음표
QUESTION_BUT: -4, //우클릭2 물음표(근데.지뢰있음)
FLAG_BUT:-5, //우클릭1 깃발(근데.지뢰있음)
CLICKED_MINE: -6, //지뢰를클릭함.
OPENED :0, // 통과.
}
숫자로 표시하면 헷갈리니까 상태코드로 만들어서
// 입력받은 값으로 칸 만들고 지뢰도 심어주는 함수.
const plantMine = (row, col, mine)=>{
console.log(row,col,mine);
const numArray = Array(row*col).fill().map((arr,i)=>{ return i }); //배열 0~99
const shuffle=[];
while( numArray.length > ((row*col)-mine) ){ // 100>80 // 99-- > 80
const beMine = numArray.splice(Math.floor(Math.random()*numArray.length), 1)[0];
//20개의 랜덤숫자(지뢰 위치가 될)
shuffle.push(beMine);
}
//칸만들어서 전부다 기본으로 채운다
const data = [];
for (let i=0; i<row; i++){
const rowData = [];
for(let j=0; j<col; j++){
rowData.push(TD_STATE.NORMAL);
}
data.push(rowData);
}
for( let k=0; k<shuffle.length; k++){ //지뢰갯수만큼.
const ver = Math.floor(shuffle[k] / cell); //몇번째줄
const hor = shuffle[k] % col; //몇번째칸
data[ver][hor]=TD_STATE.MINE; //지뢰를 심음
}
return data;
}
(0) Array(100) 0~99배열만들고,
while반복문으로 100-- > (100-지뢰갯수) 돌리면서, 랜덤 20개 숫자 구함
(1)입력받은 값으로 2차원 배열 만들고, [ -1,-1,-1,-1,-1, ], [ -1,-1,-1,-1,-1, ], [ -1,-1,-1,-1,-1, ] ...
(2)지뢰갯수에맞게 지뢰도 랜덤으로 심어준다 [ -1,-1,-7,-1,-1, ], [ -1,-7,-1,-7,-1, ], [ -1,-1,-1,-1,-1, ] ...
화면그리기
Table, Tr, Td 컴포넌트로 context API를 사용해서 화면을 그린다
Table -> Tr, Tr -> Td 이런식으로 props로 물려 물려 주지않고
(1)Table , Tr 컴포넌트 둘다 useContext를 이용하여 createContext로 만든TableContext를 가져온다
거기에 있는 tableData 를 분해구조할당으로 가져옴!
(2) tableData 를 이용해 JSX 를 사용해 화면 그린다
import React, { useContext } from 'react';
import Tr from './Tr';
import { TableContext } from './Mine'; // (1)
const Table = () =>{
const { tableData } = useContext(TableContext); // (1)
return(
<table>
<tbody> // (2)
{Array(tableData.length).fill().map( (tr, i)=>(<Tr trIndex={i}/>) )}
</tbody>
</table>
);
}
export default Table;
마찬가지로 Tr컴포넌트도 useContext로 tableData 를 사용
import React, { useContext } from 'react';
import Td from './Td';
import { TableContext } from './Mine';
const Tr = ({trIndex}) =>{
const { tableData }= useContext(TableContext);
console.log( tableData[0]);
return(
<tr>
{ tableData[0] && Array(tableData[0].length).fill()
.map( ( td, i )=><Td trIndex={trIndex} tdIndex={i}/> ) }
</tr>
);
}
export default Tr;
Table에서 Tr에게 props로 tr index 넘겨주고,
Tr에서 Td에게 받은 tr inde 랑, td index 넘겨줘서
Td는 [trIndex][tdIndex]로, Context의 tableData 값대로 화면에 값을 보여준다
//Td.jsx
import React ,{useContext}from 'react';
import {TableContext} from './Mine';
const Td = ({trIndex, tdIndex}) =>{
const{ tableData } = useContext(TableContext);
return(
<td>{tableData[trIndex][tdIndex]}</td>
);
}
export default Td;
번호상태값 받아서 스타일, 텍스트 바꾸기
상태코드stateCode -1,-7를 받아서
style적용과 어떤text출력할지 만들어주는 함수를 만든다
import React ,{useContext}from 'react';
import {TableContext} from './Mine';
const tdStyle = ( stateCode ) =>{
};
const tdText = ( stateCode ) =>{
}
const Td = ({trIndex, tdIndex}) =>{
const{ tableData } = useContext(TableContext);
const stateCode = tableData[trIndex][tdIndex]; //-1, -7
return(
<td style={tdStyle(stateCode)}>
{tdText(stateCode)}
</td>
);
}
export default Td;
원하는 배경색상, 텍스트를 표시를 지정
Mine컴포넌트에서 export한 TD_STATE를 가져와서 사용
TD_STATE.NORMAL이면, 즉 -1이면
const tdStyle = ( stateCode ) =>{
switch( stateCode ){
case TD_STATE.NORMAL:
case TD_STATE.MINE:
return{
background:'#6b6b6b'
}
case TD_STATE.OPENED:
return{
background: '#fff'
}
}
};
const tdText = ( stateCode ) =>{
switch(stateCode){
case TD_STATE.NORMAL:
return '';
case TD_STATE.MINE:
return 'X';
default:
return '';
}
}
// Mine.jsx
import React ,{ useEffect, useReducer, createContext, useMemo }from 'react';
import Form from './Form';
import Table from './Table';
export const TD_STATE = {
MINE:-7, //지뢰심어짐
NORMAL:-1, //기본
FLAG: -3, //우클릭1 깃발
QUESTION:-2, //우클릭2 물음표
QUESTION_BUT: -4, //우클릭2 물음표(근데.지뢰있음)
FLAG_BUT:-5, //우클릭1 깃발(근데.지뢰있음)
CLICKED_MINE: -6, //지뢰를클릭함.
OPENED :0, // 통과.
}
// 입력받은 값으로 칸 만들고 지뢰도 심어주는 함수.
const plantMine = (row, col, mine)=>{
const numArray = Array(row*col).fill().map((arr,i)=>{ return i }); //배열 0~99
const shuffle=[];
while( numArray.length > ((row*col)-mine) ){ // 100>80 // 99-- > 80
const beMine = numArray.splice(Math.floor(Math.random()*numArray.length), 1)[0];
//20개의 랜덤숫자(지뢰 위치가 될)
shuffle.push(beMine);
}
//칸만들어서 전부다 기본으로 채운다
const data = [];
for (let i=0; i<row; i++){
const rowData = [];
for(let j=0; j<col; j++){
rowData.push(TD_STATE.NORMAL);
}
data.push(rowData);
}
for( let k=0; k<shuffle.length; k++){ //지뢰갯수만큼.
const ver = Math.floor(shuffle[k] / col); //몇번째줄
const hor = shuffle[k] % col; //몇번째칸
data[ver][hor]=TD_STATE.MINE; //지뢰를 심음
}
return data;
}
export const TableContext = createContext({
tableData:[],
dispatch: () => {},
});
const initialState = { //2
tableData:[],
timer:0,
result:0,
}
export const START_GAME = 'START_GAME';
const reducer = (state,action)=>{ //3
switch (action.type){
case START_GAME:
return{
...state,
tableData: plantMine(action.row, action.col, action.mine)
};
default:
return state;
}
}
const Mine = () =>{
const [state, dispatch] = useReducer( reducer, initialState); //1
const { timer , result , tableData } = state;
//const tblContextValue = useMemo( ()=>{ tableData: tableData, dispatch } ,[tableData]);
const tblContextValue = useMemo( ()=>({ tableData, dispatch }) ,[tableData]);
return(
<TableContext.Provider value={tblContextValue}>
<h3>지뢰찾기</h3>
<Form />
<Table />
<p>타이머:{timer}</p>
<p>결과:{result}</p>
</TableContext.Provider>
)
}
export default Mine;
//Form.jsx
import React ,{ useState, useCallback ,useContext} from 'react';
import { TableContext ,START_GAME } from './Mine'; //
const Form = () =>{
const [ row, setRow ] = useState(10);
const [ col, setCol] = useState(10);
const [ mine, setMine] = useState(20);
const { dispatch } = useContext(TableContext);
const onChangeRow = useCallback( (e) =>{
setRow(e.target.value);
});
const onChangeCol = useCallback((e) =>{
setCol(e.target.value);
});
const onChangeMine = useCallback((e) =>{
setMine(e.target.value);
});
const onClickButton = useCallback( () =>{
dispatch({ type:START_GAME, row, col, mine });
}, [row, col, mine]);
return(
<>
<input type="number" placeholder="가로칸 갯수" value={ row} onChange={onChangeRow} />
<input type="number" placeholder="세로칸 갯수" value={ col} onChange={onChangeCol} />
<input type="number" placeholder="지뢰 갯수" value={ mine} onChange={onChangeMine} />
<p><button onClick={onClickButton}>게임시작</button></p>
</>
);
}
export default Form;
//Table.jsx
import React, { useContext } from 'react';
import Tr from './Tr';
import { TableContext } from './Mine';
const Table = () =>{
const { tableData } = useContext(TableContext);
return(
<table>
<tbody>
{Array(tableData.length).fill().map( (tr, i)=>(<Tr trIndex={i}/>) )}
</tbody>
</table>
);
}
export default Table;
//Tr.jsx
import React, { useContext } from 'react';
import Td from './Td';
import { TableContext } from './Mine';
const Tr = ({trIndex}) =>{
const { tableData }= useContext(TableContext);
console.log( tableData[0]);
return(
<tr>
{ tableData[0] && Array(tableData[0].length).fill()
.map( ( td, i )=><Td trIndex={trIndex} tdIndex={i}/> ) }
</tr>
);
}
export default Tr;
//Td.jsx
import React ,{useContext}from 'react';
import {TableContext, TD_STATE } from './Mine';
const tdStyle = ( stateCode ) =>{
switch( stateCode ){
case TD_STATE.NORMAL:
case TD_STATE.MINE:
return{
background:'#6b6b6b'
}
case TD_STATE.OPENED:
return{
background: '#fff'
}
}
};
const tdText = ( stateCode ) =>{
switch(stateCode){
case TD_STATE.NORMAL:
return '';
case TD_STATE.MINE:
return 'X';
default:
return '';
}
}
const Td = ({trIndex, tdIndex}) =>{
const{ tableData } = useContext(TableContext);
const stateCode = tableData[trIndex][tdIndex]; //-1, -7
return(
<td style={tdStyle(stateCode)}>
{tdText(stateCode)}
</td>
);
}
export default Td;
'React > basic' 카테고리의 다른 글
14 (3)context API (0) | 2020.07.30 |
---|---|
14 (2)context API (0) | 2020.07.29 |
13. (3) useReducer (0) | 2020.07.29 |
13. (2) useReducer (0) | 2020.07.29 |
13. useReducer (0) | 2020.07.28 |