개요

이 글은 강좌가 아닙니다. 부족한 부분이 있더라도, 너그럽게 봐주신다면 감사하겠습니다. 피드백 언제나 환영입니다 :)

이번 글에서는 구조가 되게 많이 바뀌었어요. 기존에 matrix라는 배열을 통해 빙고판을 관리했는데 matrix 배열이 state값도 아니다 보니, 값을 건드려도 리렌더링이 되지 않더군요. 그래서 상태관리를... 하기로... 했습니다...

기존에 함수형으로 구현되어 있는 컴포넌트를 다시 클래스형으로 바꾸긴 싫어서 mobx-react-lite 라는 모듈을 사용할까 했어요. 하지만 문서를 읽어봤는데 그렇게 좋다고 생각이 들지 않았어요. inject나, Provider 같은 기능도 직접 구현해야했고, 무엇보다 문서가 눈에 잘 안들어왔어요.

React Hooks를 잘 사용하기로 마음먹었으니 Context API 라는 기능을 통해 상태관리를 한번 해볼거에요.
Redux를 잘 사용하진 못하지만, 최대한 Redux처럼(?) 사용해볼거에요.

아 맞다. 그리고 제가 프로젝트를 eject 시켜 버렸는데 그래서 그런지
스크린샷 2019-11-19 오후 5.48.10.png

javascript로 인식하네요. 굳이 eject 할 이유도 없었는데 돌아갈수 없는 강을 건넌 느낌이에요. 자 일단 시작해보겠습니다.

BingoContext 파일 생성

기존에 있던 함수들까지 적으면 굉장히 길어질거 같으니, 새로 짠 함수만 적도록 할게요.

src/contexts 폴더를 만들고 그안에 BingoContext.tsx 파일을 만들어주세요.

export const INIT = "bingo/init";
export const SHUFFLE = "bingo/shuffle";
export const SET_COUNT = "bingo/count";
export const CLICK = "bingo/click";

const handleClick = (location: Location, matrix: Square[][]) => {
  const { row, column } = location;
  matrix[row][column].checked = true;
};

const BingoReducer = (state = initialState, action: any) => {
  switch (action.type) {
    case INIT:
      return { ...state, matrix: initialize(state.count) };
    case SHUFFLE:
      shuffle(action.payload, state.matrix, state.count);
      return { ...state };
    case SET_COUNT:
      return { ...state, count: action.payload };
    case CLICK:
      handleClick(action.payload, state.matrix);
      return { ...state };
    default:
      throw new Error();
  }
};

const BingoContext = createContext([] as any);

const BingoProvider: React.FC<React.ReactNode> = ({ children }) => {
   const contextValue = useReducer(BingoReducer, initialState);
   return (
      <BingoContext.Provider value={contextValue}>
         {children}
      </BingoContext.Provider>
   );
};

const useBingo = () => {
   const contextValue = useContext(BingoContext);
   return contextValue;
};

export { BingoProvider, useBingo };

handleClick 이란 함수를 통해 클릭 이벤트를 제어해줍니다.

src/contexts 폴더 안에 index.ts 파일을 만들어줘요.

import React from "react";

import { BingoProvider } from "./BingoContext";

const Provider: React.FC<any> = ({ children }) => (
   <BingoProvider>{children}</BingoProvider>
);

export default Provider;

솔직히 BingoContext 하나만 만들어서 필요없을수도 있지만, 그래도 만들어봤어요. 만약에 Context가 여러개라면

const Provider: React.FC<any> = ({ children }) => (
    <ProviderOne>
      <ProviderTwo>{children}</ProviderTwo>
    </ProviderOne>
);

이런식으로 사용하면 돼요.

BingoBoard.tsx 파일 수정

import { useBingo, INIT, CLICK } from "../contexts/BingoContext";

const [state, dispatch] = useBingo();
const { count, matrix } = state;

useEffect(() => {    // count가 변경할 때 마다, 빙고판을 새로 그려줍니다.
  dispatch({ type: INIT });
}, [count, dispatch]);

const handleClick = useCallback(    // 아이템 클릭 이벤트
  (row: number, column: number) => {
    dispatch({ type: CLICK, payload: { row, column } });
  },
  [dispatch]
);

return(
    ...
    <BingoItem
      ...props,
      handleClick={handleClick}
    />  
    ...
)

BingoItem.tsx 파일 수정

const Item = styled("div")<ItemProps>`
   display: flex;
   align-items: center;
   justify-content: center;
   width: 80px;
   height: 80px;
   background-color: ${props => (props.checked ? "#bbb" : "#fff")}    // 빙고가 클릭되었다면, 
   font-size: 3.5rem;
   cursor: pointer;
`;

return(
    <Item 
      checked={checked} 
      onClick={()=> handleClick(rowNumber, columnNumber)}    // props로 넘어온 rowNumber, ColumnNumber을 넘김
    >
        <Emoji contents={contents}></Emoji>
    </Item>
)

안보이던 컴포넌트가 보이죠?? 바로 <Emoji /> 이건데요. 이모지를 띄울때 role="img" 속성을 추가하란 경고창이 떠서 이모지를 띄우는 컴포넌트를 만들었어요.

Emoji.tsx 파일 생성

src/componentsEmoji.tsx파일을 만들어줘요.

import React from "react";

const Emoji: React.FC<any> = ({ contents }) => (
   <span role="img">{contents}</span>
);

export default Emoji;

되게 별거 없어요 ㅋㅋㅋㅋ

자, 그럼 결과물을 한번 볼까요?

결과물

스크린샷 2019-11-19 오후 5.42.18.png

이렇게 클릭한 아이템들이 회색으로 변합니다. 이번 글에서는 되게 많은 코드들이 바뀌었고, 구조도 많이 바뀌었는데 이해가 잘 가게 글을 썼는지 모르겠네요... :( Context Api도 저렇게 쓰는게 맞는지... 더 좋은 방법이 있을거 같은 느낌이 들어요.

다음에는

다음 글에서는 빙고를 체크(?) 하는 기능을 추가할거에요.

저장소

모든 코드와 진행과정은 https://github.com/mango906/EmojiBingo 에서 확인 가능합니다.