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) 완벽히 내껄로 할려면 몇번 봐야겠지만
//재밌었슴다.