사상 두번째 프로젝트 - #2 기능 추가

sham·2021년 12월 10일
0

Betti 개발일지

목록 보기
3/14

기본적인 메인 페이지의 틀은 완성했으니 기능을 추가할 차례다.

구현 과정

구조

메인 페이지는 여전히 두 개의 컴포넌트로 구성되었으나, 모듈화를 진행하였다.

TeamBar

MainScreen

중요

useReducer 적용

import { React, useEffect, useReducer } from 'react';
import { TeamBar, MainScreen } from '../../components';
import './Main.scss';

const initalState = {
  stateData: {
    curTeam: 0,
    curTest: 0,
  },

  teamData: [
    { index: 0, name: 'asg Team', test: ['qwre', 'asdg', 'asd'] }, // 각 테스트는 객체 형태여야 함.
    { index: 1, name: 'qwer Team', test: ['test1', 'test2', 'test3'] },
  ],
};

const reducer = (state, action) => {
  const stateData = state.stateData;
  const teamData = state.teamData;
  switch (action.type) {
    case 'addTeam': {
      const newTeamIndex = teamData.length;
      const newTeamName = action.name;
      const newTeam = { index: newTeamIndex, name: newTeamName, test: [] };
      stateData.curTeam = newTeamIndex;
      state.teamData.push(newTeam);
      return { ...state };
    }
    case 'addTest': {
      console.log('실행됨!');

      const curTeamIndex = action.curTeam; // state에서 끌어다 써도 될 듯
      const testName = action.newTestName;
      let newArray = teamData[curTeamIndex].test;
      const temp = [...newArray, testName];
      // newArray.push(testName);
      teamData[curTeamIndex].test = temp;
      console.log(...newArray);
      return { ...state };
    }
    case 'changeTeam': {
      stateData.curTeam = action.dst;
      stateData.curTest = 0;
      return { ...state };
    }
    case 'changeTest': {
      stateData.curTest = action.dst;
      return { ...state };
    }
    default: {
      return state;
    }
  }
};

const Main = () => {
  const [state, dispatch] = useReducer(reducer, initalState);
  const curTeam = initalState.stateData.curTeam;
  const curTest = initalState.stateData.curTest;

  const teamData = state.teamData;

  useEffect(() => {
    console.log(state);
  }, [state]);

  // 여기서 라우팅을 해?
  return (
    <>
      {teamData && (
        <div className="page-main">
          <TeamBar teamData={teamData} dispatch={dispatch} />
          <MainScreen
            teamData={teamData}
            curTeam={curTeam}
            curTest={curTest}
            dispatch={dispatch}
          />
        </div>
      )}
    </>
  );
};

export default Main;

useReducer에 대해서는 링크 참조.

useReducer를 이용해서 state를 전역적으로 관리하게끔 하였다.

dispatch를 props로 보내면 자식 프로세스에서는 dispatch를 action 객체 형태로 정제해서 보낸다. 어디에서는 dispatch를 가지고 있다면 reducer 함수에서 받아서 case에 맞는 작동을 취한다.

모달창 만들기

  • src/TeamBar/TeamBar.jsx
import { React, useState } from 'react';
    import TeamBarHeader from './TeamBarHeader';
    import TeamBarBody from './TeamBarBody';
    import Modal from '../../util/Modal';
    import useModalHandle from '../../util/hooks/useModalHandle';
    import './TeamBar.scss';
    
    const TeamBar = ({ teamData, dispatch }) => {
      const [open, close, isOpen] = useModalHandle();
      const [teamName, setTeamName] = useState('');
    
      const changeName = e => {
        setTeamName(e.currentTarget.value);
      };
      const addTeamEvent = e => {
        e.preventDefault();
        if (teamName === '') return;
        dispatch({ type: 'addTeam', name: teamName });
        setTeamName('');
        close();
      };
    
      const content = (
        <div className="team-modal">
          <form action="input" onSubmit={addTeamEvent}>
            <div>Create New Team</div>
            <div>Name Your Team Before choosing a new project.</div>
            <input
              onChange={changeName}
              maxLength="12"
              type="text"
              value={teamName}
            />
          </form>
        </div>
      );
    
      return (
        <div className="team-bar">
          <TeamBarHeader />
          <TeamBarBody teamData={teamData} dispatch={dispatch} open={open} />
          <Modal isOpen={isOpen} close={close} content={content} />
        </div>
      );
    };
    
    export default TeamBar;
  • util/hooks/useModalHandle.jsx
    import { useState } from 'react';
    
    const useModalHandle = () => {
      const [isOpen, setIsOpen] = useState(false);
    
      const open = () => {
        setIsOpen(true);
      };
      const close = () => {
        setIsOpen(false);
      };
    
      return [open, close, isOpen];
    };
    
    export default useModalHandle;

커스텀 훅으로 만들어 놓은 useModalHandle을 써서 모달을 관리할 수 있는 isOpen, 참과 거짓으로 값을 바꾸는 콜백함수 open,close를 받는다.

content라는 객체에 컴포넌트를 통째로 넣어주고 Modal 컴포넌트의 props로 보낸다. isOpen이 참일 경우 content를 렌더링 할 것이다.

  • util/Modal.jsx
    import React from 'react';
    
    const Modal = ({ isOpen, close, content }) => {
      const isOuter = e => {
        const clicked = e.target.className;
        if (clicked === 'modal') close();
      };
      return (
        <>
          <>
            {isOpen && (
              <>
                <div className="modal" onClick={isOuter}>
                  {' '}
                  {content}
                </div>
              </>
            )}
          </>
        </>
      );
    };
    
    export default Modal;

재사용하기 용이한 모달창 틀을 만들었다.

전체를 감싸는 div 태그를 하나 만들고 이벤트를 걸어서 해당 태그를 클릭한 경우 모달창을 끄게끔 closeisOpen을 거짓으로 바꾼다.

이슈

test가 두 개씩 늘어나는 이슈

const reducer = (state, action) => {
  const stateData = state.stateData;
  const teamData = state.teamData;
  switch (action.type) {
    case 'addTest': {
      console.log('실행됨!');

      const curTeamIndex = action.curTeam; // state에서 끌어다 써도 될 듯
      const testName = action.newTestName;
      let newArray = teamData[curTeamIndex].test;
      const temp = [...newArray, testName];
      console.log(temp);
      teamData[curTeamIndex].test = temp;
      console.log(teamData[curTeamIndex].test);
      return { ...state };
    }
    default: {
      return state;
    }
  }
};

addTest 액션이 한 번 실행되는 동안 state가 두 개씩 늘어나는 이슈 발생. 코드를 아래와 같이 바꾸었더니 해결. 원인은 아직 분석하지 못했으나 원래 state를 참조하는 과정에서 생긴 이슈같다.

const reducer = (state, action) => {
  const stateData = state.stateData;
  const teamData = state.teamData;
  switch (action.type) {
     case 'addTest': {
      console.log('실행됨!');

      const curTeamIndex = action.curTeam; // state에서 끌어다 써도 될 듯
      const testName = action.newTestName;
      const temp = [...action.curTestArray, testName];
      // newArray.push(testName);
      teamData[curTeamIndex].test = temp;
      return { ...state };
    }
    default: {
      return state;
    }
  }
};
profile
씨앗 개발자

0개의 댓글