[React] 캘린더 컴포넌트 만들기 3

Chloé·2021년 3월 23일
0
post-custom-banner

자식에서 부모로 데이터 전달하기

캘린더 본문을 만들기 위해, 컴포넌트간 데이터 전달이 필요해졌다.
지금 만들고 있는 캘린더는 전체가 캘린더라는 커다란 컴포넌트로 감싸져 있고, 그 아래 헤더(해당월 지정하는 부분)와 바디(달력 본문)라는 두 개의 컴포넌트가 들어온다.

function Calendar() {
  
  return (
    <div className="calWrapper">
      <Header />
      <Body />
    </div>
  );
  
}

export default Calendar;

이 때 헤더에서 달력의 월을 앞뒤로 전환하면, 이를 바디로 전달해야 한다. 즉, 자식 컴포넌트에서 다른 자식 컴포넌트로 데이터를 전달해야 한다. 이를 위해, 헤더(자식) -> 캘린더(부모) -> 바디(자식) 순으로 데이터를 전달하고자 한다.

💡 함수형 컴포넌트 : useState 사용하기

리액트의 컴포넌트는 클래스형과 함수형으로 나뉜다. 컴포넌트간 데이터 전달을 검색해보면 주로 클래스형 컴포넌트의 경우를 소개한다. 함수형으로 사용하기 위해서는 useState 라는 Hook을 활용해준다.

부모 컴포넌트에 useState 정의하기

const [baseDate, setBaseDate] = useState(moment());
  • 부모 컴포넌트에 useState를 이용해 baseDate라는 변수를 정의했다.
  • 이 변수를 변경하는 함수는 setBaseDate이다.
  • 초기값은 moment 라이브러리를 이용하여 오늘 날짜로 지정했다.

자식 컴포넌트로 함수 전달하기

  return (
    <div className="calWrapper">
      <Header setBaseDate={ setBaseDate } />
    </div>
  );
  • setBaseDate를 자식 컴포넌트로 전달한다.

자식 컴포넌트에서 props로 함수 받기

  • 자식 컴포넌트에서는 props를 사용하여 부모로부터 전달받은 데이터를 사용할 수 있다.
  • onClick을 이용해 버튼을 클릭할 때 마다 setBaseDate 함수를 동작시켜, 변경된 정보를 부모의 baseDate로 설정하고자 한다.
function Calendar(props) {

  return (
    <div className="calHeaderWrapper">
      <div className="calHeaderBtn calHeaderBeforeBtn">
        <LeftOutlined 
          style={{ color: "#EFBF43"}}
          onClick={() => { 
            setCurDate(curDate.add(1, 'months')); 
            props.setBaseDate(curDate); 
          }}
        />
      </div>
    </div>
  );
}

export default Calendar;
  • 버튼을 누를 때마다 curDate가 변경되는데, 이를 setBaseDate에 넣어 부모 컴포넌트로 전달한다.

다른 자식 컴포넌트로 데이터 전달하기

function Calendar() {
  
  const [baseDate, setBaseDate] = useState(moment());

  return (
    <div className="calWrapper">
      <Header setBaseDate={ setBaseDate } />
      <Body baseDate={ baseDate } />
    </div>
  );  
}

이렇게 헤더에서 설정된 baseDate를 부모 컴포넌트인 Calendar를 경유하여, 형제(?) 컴포넌트인 바디로 전달할 수 있다.

💥 더 알아볼 것 : 리액트에서 컴포넌트간 데이터 전달

이 과정에서 세 가지 의문점이 발생했다. 여러가지로 살펴보았으나 해결하지 못해서 나중을 위해 적어둔다.

1. 부모 컴포넌트에서 정의한 변수는 단일한 자식 컴포넌트로만 전달할 수 있다?

즉, 하나의 변수는 한 개의 자식 컴포넌트에만 전달할 수 있다.

오류 코드

  return (
    <div className="calWrapper">
      <Header baseDate={ baseDate } />
      <Body baseDate={ baseDate } />
    </div>
  );
  • 하나의 변수를 두 개의 자식 컴포넌트에 모두 전달하려고 해보았다. Header에서는 baseDate를 문제 없이 전달받았으나, Body에서는 props.baseDate를 확인하면 undefined가 발생했다.
  • baseDate를 헤더로 전달하지 않고 바디로 바로 전달하자 문제 없이 전달받을 수 있었다.

코딩을 잘못 한 것인지, 리액트가 원래 이런 식으로 작동하는 것인지는 알 수 없지만 이를 염두에 두고 컴포넌트를 기획해야겠다.

2. 자식 컴포넌트에서 변수와 useState 함수를 모두 받아오는 경우, 부모 컴포넌트에서 데이터가 변경되지 않는다?

<Header baseDate={ baseDate } setBaseDate={ setBaseDate } />
  • 이렇게 헤더에서 baseDatesetBaseDate를 모두 전달받았다.
  • 이후
    1. baseDatecurDate의 초기값으로 설정하고,
    2. setCurDate로 변경한 뒤,
    3. setBaseDate를 이용해 부모로 전달하려고 시도했다.
  • 부모 컴포넌트로부터 데이터를 무사히 전달(1)받았고, 변경(2)까지 완료됐으나, 다시 부모 컴포넌트로 전달(3)되지 않는다.

리액트에서 복수의 props를 사용하는 것이 가능하기 때문에 전달 과정에서의 문제는 아닌 것 같은데, 어디에서 state 변경이 잘못 된 것인지 이유를 알 수 없었다. 우선은 초기값을 다시 설정하는 것으로 넘어갔다.

3. 메소드를 이용해 props의 상태를 변경하면, 자식 컴포넌트 내에서의 모든 props가 영향을 받는다?

  const curDate = props.baseDate
  const firstDateOfMonth = curDate.startOf('month')
  • 자식 컴포넌트에서 오늘에 해당하는 baseDate를 받아온 다음,
  • moment 라이브러리의 startOf 메소드를 사용해 그 달의 첫 날을 구하고자 한다.
  • 예를 들어, 부모 컴포넌트인 캘린더에서 moment()로 주어진 baseDate가 2021년 3월 23일이라면,
    • 이를 전달받아 설정된 curDate는 2021년 3월 23일이며,
    • startOf를 적용한 firstDateOfMonth는 2021년 3월 1일로 설정되어야 한다.
  • 그런데 이 메소드를 적용하고 났더니 curDate까지도 3월 1일로 저장되는 이상한 현상이 발생했다.

clone()으로 해결

  const curDate = props.baseDate
  const firstDateOfMonth = curDate.clone().startOf('month')
  • clone() 메소드로 curDatefirstDateOfMonth를 분리하는 데 성공했다.

당장 캘린더를 구현하는 데 큰 지장은 없을 것 같아서 우선은 넘어가는 내용들이지만, 나중에 리액트를 좀 더 이해하고 공부하며 다시 짚어보고 싶은 내용이라 기록해 둔다.

profile
클로이 데일리 로그
post-custom-banner

0개의 댓글