24/1/18 지뢰찾기 <Mine Search>(2)
2024. 1. 19. 08:59ㆍ카테고리 없음
<Form.jsx> 지뢰찾기(1)의 해석은 엉망이었지만 이건 열심히 해볼께요 느낌아니까 ㅋㅋㅋ
import React, { useState, useCallback, useContext, memo } from "react"; //훅들 임포트
import { TableContext, START_GAME } from "./MineSearch"; // case 종류 상수선언한거 임포트
import styled from "styled-components"; //마인서치에서 만든 table Context를 임포트
const Form = memo(() => { //오우 메모이제이션이다. 컴포넌트 전체를 메모로 요래 감싸서
const [row, setRow] = useState(10); // 세로 //부모컴포넌트의 랜더링으로부터 보호하고 Form 태그에
const [cell, setCell] = useState(10); // 가로 //직접적인 state 변화가 없는한 리랜더링이 되지 않죠
const [mine, setMine] = useState(15); // 지뢰 개수
const { dispatch } = useContext(TableContext); //지금 유즈스테이트 형태를 보니까 cell이 가로고
//row 가 세로구나... 유즈컨택스트에 dispatch를 꺼내서 상태변화를 // useCallback --> 불필요한 렌더링 방지 //기록시킬 예정이겠고
const onChangeRow = useCallback((e) => { //유즈콜백으로 Row는 첫 값을 유지하는구먼
setRow(e.target.value); //아하 알겠다. 마운트 됐을때 딱 정해진 가로 세로 지뢰개수는
}, []); //리랜더링 되면서 변할필요가없응께 메모이제이션하신거군 오호
const onChangeCell = useCallback((e) => {
setCell(e.target.value);
}, []);
const onChangeMine = useCallback((e) => {
setMine(e.target.value);
}, []);
const onClickBtn = useCallback(() => { //버튼 클릭하면 인풋에 입력받은 값으로 세팅하는군
dispatch({ type: START_GAME, row, cell, mine }); //스위치 케이스문에 start game일때를 일으키고 흐음
}, [row, cell, mine]);
return (
<StDiv> //여기서 지뢰찾기에 가로 세로 지뢰개수를 입력받는구먼
<StInput
type="number"
placeholder="세로"
value={row}
onChange={onChangeRow} //벨류와 온체인지 유즈스테이트에 값 저장 하고
/>
<StInput
type="number"
placeholder="가로"
value={cell}
onChange={onChangeCell}
/>
<StInput
type="number"
placeholder="지뢰"
value={mine}
onChange={onChangeMine}
/>
<StBtn onClick={onClickBtn}>시작</StBtn> //클릭버튼으로 그값을
</StDiv>
);
});
export default Form;
const StDiv = styled.div`
display: flex;
margin-top: 50px;
justify-content: center;
`;
const StInput = styled.input`
width: 100px;
height: 30px;
font-size: 22px;
margin-right: 22px;
text-align: center;
border: 0;
text-indent: 17px;
border-bottom: 3px solid #000;
`;
const StBtn = styled.button`
width: 100px;
font-size: 17px;
font-weight: bold;
`;
//이해 안되시면 지뢰찾기 1편 보고오세요 ㅋ
<Table.jsx> 가로 세로와 그 안에 값을 가지는 테이블 그자체인듯
import React, { useContext, memo } from "react";
import { TableContext } from "./MineSearch"; //여기서도 컨텍스트 만든거 가져오고
import Tr from "./Tr"; //여기 tr컴포넌트 있고
import styled from "styled-components";
const Table = memo(() => { //여기도 메모이 제이션
const { tableData } = useContext(TableContext); //useContext를 사용해서 전역상태관리를 위한 준비
return (
<StTable>
{Array(tableData.length) //오오 신기하다 Array인데 tableData의 길이만큼의 요소를 갖는배열인데
.fill() //소괄호에 아무것도 없네? 빈값 채우는 건가?
.map((tr, i) => ( //어차피 맵매서드 쓸꺼라 상관없나보네
<Tr rowIndex={i} /> //인덱스 프롭스로 Tr컴포넌트에 주는구나 Tr컴포넌트가 테이블데이터의 길이만큼
))} //만들어 질테고 호호
</StTable>
);
});
export default Table;
const StTable = styled.table`
border-collapse: collapse;
margin: 20px auto;
`;
<Tr.jsx>
import React, { useContext, memo } from "react";
import { TableContext } from "./MineSearch"; //역시나 여기도 전역관리를 위해서 임포트
import Td from "./Td"; //Td 컴포넌트 임포트
const Tr = memo(({ rowIndex }) => { //불필요한 랜더링 막는 메모이제이션
const { tableData } = useContext(TableContext); //이하동문
//아하 프롭스로 받은 인덱스는 rowindex군 ㅇㅋㅇㅋ return (
<tr>
{tableData[0] &&
Array(tableData[0].length) //table데이터의 [0]번째의 길이만큼 ㅇㅋ 어차피 몇번째 요소든 길이는 같겠지
.fill()
.map((td, i) => <Td rowIndex={rowIndex} cellIndex={i} />)} //Td컴포넌트의 다시 프롭스 주고 요번에는
</tr> //cellindex까지 주고 ㅇㅋㅇㅋ
);
});
export default Tr;
<Td.jsx> 아니 여기 갑자기 왤케 길어?
import React, { useContext, useCallback, memo } from "react";
import { CODE, TableContext } from "./MineSearch"; .//CODE인 케이스 쓸 예정인가보넹
import styled from "styled-components";
import {
OPEN_CELL,
CLICK_MINE,
FLAG_CELL, //케이스들 다 쓸껀가 보네
QUESTION_CELL,
NORMAL_CELL,
} from "./MineSearch";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; //폰트어썸 또 등장
import { faBomb, faFlag, faQuestion } from "@fortawesome/free-solid-svg-icons"; //수인님 CSS짱 잘하시네 홀리
const Td = memo(({ rowIndex, cellIndex }) => { //프롭스 받아주고
const { tableData, dispatch, halted } = useContext(TableContext); //이번엔 3가지 속성이 필요한가보군
//dipatch 는 state변화 줄애고 나머지는 그냥 데이터 const onClickTd = useCallback(() => {
if (halted) { //Td누르면 정지상태이면 안눌리고
return;
}
switch (tableData[rowIndex][cellIndex]) { //오우 쉣 tableData의 각 가로세로 위치에 따른 케이스라 ㅎㄷㄷ
case CODE.OPENED: //앞전에 지뢰찾기 (1)때로직 들은 그냥 밑바탕이고
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; //평상시일때는 눌리면 cell을 여는 구먼 열릴 row cell정보 보내주고 state변경
case CODE.MINE:
dispatch({ type: CLICK_MINE, row: rowIndex, cell: cellIndex });
return; //지뢰 눌러을때 로직 row cell 정보 보내주고 이하동문
default:
return;
}
}, [tableData[rowIndex][cellIndex], halted]); //유즈콜백내부의 함수가 변하기위해 구독해야할 state들
const onRightClickTd = useCallback( //아니 useCallback 마스터시네 이분? 난 딱 한회 써봤는데 대단스
(e) => { //올바른 클릭이라는 뜻인가?
e.preventDefault(); // 디폴트로 메뉴가 뜨는것을 방지
if (halted) { //정지상태일때는 안눌리고
return;
}
switch (tableData[rowIndex][cellIndex]) { //스위치문 개 많아 ㅋㅋ
case CODE.OPENED: //이때는 안눌리고
return;
case CODE.NORMAL: //눌리고 이떄 context의 state변경 해주고
case CODE.MINE:
dispatch({ type: FLAG_CELL, row: rowIndex, cell: cellIndex }); //그때의 타입과 필요 인자들 보내주고
return;
case CODE.FLAG_MINE:
case CODE.FLAG:
dispatch({ type: QUESTION_CELL, row: rowIndex, cell: cellIndex }); //이하동문
return;
case CODE.QUESTION_MINE:
case CODE.QUESTION:
dispatch({ type: NORMAL_CELL, row: rowIndex, cell: cellIndex }); //이하동문
return;
default:
return; //디폴트 안눌리고
}
},
[tableData[rowIndex][cellIndex], halted] //이하동문
);
return ( //화면에 그려지는건 별거 없군 박스 한칸분량이겠고
<StTd
style={getTdStyle(tableData[rowIndex][cellIndex])} //스타일을 함수로 줘서 그리턴값을 받는다? 홀리쉿
onClick={onClickTd}
onContextMenu={onRightClickTd} //우와 이거 머야 onContextMenu 이거 머냐구요?
> //끝날때까지 끝난게 아니다?
{getTdText(tableData[rowIndex][cellIndex])}
</StTd>
); //챗 지피티 한테 물어보니까 onContextMenu 이거 오른쪽 마우스 이벤트래요
}); //찢었다. 와 한수 배웠습니다. 야미 옴뇸뇸뇸
export default Td;
const StTd = styled.td` //td라는 태그가 따로 원래 있는거였어요? 우와 첨봄
border: 5px solid black; //그럼 tr 태그도 있겄네여? 어디서 본것 같기도 하고
width: 40px;
height: 40px;
text-align: center;
font-weight: bold;
font-size: 24px;
`;
const getTdStyle = (code) => { //아니 평생볼 switch문 여기 다있네
switch (code) { //와 이것도 놀랍다 함수의 return을 객체로 줘서 태그속성에 직접 함수 할당
case CODE.NORMAL: //멋있어
case CODE.MINE:
return {
background: "#444",
};
case CODE.CLICKED_MINE:
return {
background: "red",
};
case CODE.OPENED:
return {
background: "white",
};
case CODE.QUESTION_MINE:
case CODE.QUESTION:
return {
background: "yellow",
};
case CODE.FLAG_MINE:
case CODE.FLAG:
return {
background: "green",
};
default:
return {
background: "white",
};
}
};
const getTdText = (code) => { //이건 Td태그에 안에 들어갈 text 선택적으로 넣어주는구나
switch (code) {
case CODE.NORMAL: //노멀이랑 마인일경우에는 뒷면일테니까 아무것도 안보일테고
return "";
case CODE.MINE:
return "";
case CODE.CLICKED_MINE: //눌린 마인은 요런 태그를 부여해서 눌린폭탄 보여주고
const boom = (
<FontAwesomeIcon icon={faBomb} style={{ fontSize: "23px" }} />
);
return boom;
case CODE.FLAG_MINE:
case CODE.FLAG: //플래그된 마인과 플래그된 노멀은
const flag = <FontAwesomeIcon icon={faFlag} />; //폰트 어썸 요 태그 보여주고
return flag;
case CODE.QUESTION_MINE:
case CODE.QUESTION:
const question = <FontAwesomeIcon icon={faQuestion} />; //여기도 뭐 비슷하고
return question;
default:
return code || ""; //디폴트는 코드 없으면 빈값 ㅇㅋ
}
};
//와 진짜 로직들 아름다웠습니다. 지뢰찾기 (1,2) 완벽히 내껄로 할려면 몇번 봐야겠지만
//재밌었슴다.