[220420_TIL] React state, props, life cycle

parkjisu6239·2022년 4월 20일
0

TIL

목록 보기
4/4

react의 state, props, lifecycle에 대해 알아보자. fucntional로 작성하였고, useEffect Hook을 사용하였다. 전체 코드는 여기

목표

  1. stateprops 제대로 알아보기
  2. lifecycle example 눈으로 확인하기

project 구조

App
⎿ Parent
   ⎿ Children

연한 파란색이 Parent , 진한 파란색이Children 이다.

state와 props

React에서 자식이 부모컴포넌트의 값을 바꾸는 방법

기본적으로 react는 단방향(부모 -> 자식)으로만 데이터가 흐른다. Vue에는 emit이라는 개념이 있지만, React에서는 그렇지 않다.
대신 react는 부모의 값을 변경할 수 있는 함수를 만들어, 그 함수 자체를 props로 내린다. 이렇게 하여 자식 컴포넌트에서 부모 컴포넌트로부터 받은 함수를 실행하여, 부모의 값을 변경하는 것이다. 여전히 데이터의 방향은 위 -> 아래를 유지하되, 변경이 가능한 것이다.

Parent Component

부모 컴포넌트에서 value를 1로 세팅한 상태이다. 그리고 버튼을 클릭하면 setState를 사용하여 이전 값에 +1을 해주도록 했다.

그리고 여기서 정의한 valueplusOne 함수를 자식 컴포넌트에 props로 내려주었다. plusOne 함수를 내려줌으로써 자식 컴포넌트에서 parent의 value를 변경할 수 있게 한다.

function Parent() {
  const [value, setValue] = useState(1)

  const plusOne = () => {
    setValue(prev => prev + 1)
  }

  return (
    <div className={ParentCss}>
      <p>parnet의 state {value}</p>
      <button onClick={plusOne}>value에 1 더하기</button>
      <Children value={value} plusOne={plusOne}/>
    </div>
  );
}

Children Component

자식 컴포넌트에서는 두가지를 비교해보려고 했다.
1. 부모의 props를 props 그 자체로 사용하는 것
2. 부모의 props를 자식 컴포넌트의 state로 사용하는 것

interface Props {
    value: number,
    plusOne: () => void,
}

function Children({value, plusOne}: Props) {
    const [childValue, setChildValue] = useState(value)

    return (
      <div className={ChildrenCss}>
        <p>parent의 props를 그대로 {value}</p>
        <p>parent의 props를 state로 {childValue}</p>
        <div>
            <button onClick={plusOne}>props의 plusOne 실행</button>
            <button onClick={() => setChildValue(prev => prev + 1)}>setState 실행</button>
        </div>
      </div>
    );
  }

위 내용을 보면 props로 받은 value를 그대로 화면에 표시한다.
그리고 childValue의 초기값으로 사용하기 위해 useState를 사용하여 state를 설정했다.

props를 그대로 사용하는 것과, state로 사용하는것에는 어떤 차이가 있을까

결과

props를 그대로 받아서 랜더하는 경우, 부모의 값이 변하면 바로 바뀐 값으로 대체 된다. 부모로부터 받은 plusOne 함수도 정상적으로 작동하고 부모의 state가 잘 바뀐다.

반면 props를 자식 컴포넌트 내에서 state로 사용한 childValue의는 props의 변화를 감지하지 못한다. 컴포넌트가 마운트 될때 최초 한번만 초기값으로 사용하고, 이후에는 완전히 독립적인 값이 된다. 그래서 props의 변화에도 동일한 값을 유지하고 있다.

자식 컴포넌트는 함수로 구성되어 있는데, 컴포넌트가 리랜더 되면 useState부터 다 새로 실행되어야할 것 같지만 그렇지 않다. 처음에 정의한 useState는 컴포넌트가 마운트 될 때만 실행된다. 이후 업데이트 될 때는 state의 값을 읽어오기만 한다.

Life Cycle

위 내용과 덧붙여 react의 생명주기에 대해 알아보자. 생명주기에서 많이 사용되는 것들은 DidMount WillMount DidUpdate WillUpdate 등이 있다. Vue에서는 Created가 따로 있던 것 같은데, Mount로 봐도 무방하다.

Class 형으로 작성된 React의 경우에는 componentDidMount 등의 이름으로 각각의 타이밍이 따로 나뉘어져있었는데, function형으로 작성하면 useEffect 라는 Hook으로 다 확인 할 수 있다. useEffect 자체의 설명은 여기를 참고

Mount

컴포넌트가 마운트되는 타이밍을 알아보기 위해서는 아래처럼 작성해주면 된다.

useEffect(() => {
    console.log('parent mounted')
    return () => {
      console.log('parent will unmount')
    }
  }, [])

useEffect의 두번째 인자는 디펜던시 배열을 의미하는데, 이 배열을 비워주면 최초로 컴포넌트가 마운트 될때만 실행된다. 즉, DidMounted 의 의미로 사용이 가능하다.

또, useEffect 안에 return을 넣을 수 있다. 이때 return 함수의 의미는 해당 Hook이 종료되는 타이밍에 실행된다. 즉, WillUnMount(언마운트 후에 바로 마운트가 다시 될 것이기 때문에, WillMount 이기도 하다. 하지만 전자가 더 맞다.) 의 의미이다.

Update

업데이트는 컴포넌트내에 어느것이라도 업데이트가 되면 실행되게 하거나, 특정 변수가 변경될 때만 실행되게 할 수 있다.

useEffect(() => {
    console.log('parent updated')
    return () => {
      console.log('parent will update')
    }
  })

두번째 인자에 아무것도 넣지 않으면, update될때 마다 실행되게 할 수 있다. 마찬가지로 return에 함수를 넣어주면, 이 함수는 clean-up 하는 타이밍에 실행되어 will update의 의미가 된다.

useEffect(() => {
    console.log('parent state updated', value)
    return () => {
      console.log('parent state will update', value)
    }
  }, [value])

위 처럼 디펜던시 배열에 특정 값을 넣어주면, 그 값이 변경될때만 실행하게 할 수 있다.

콘솔 확인

부모컴포넌트, 자식 컴포넌트 모두 Mount, Update를 확인하기 위해 useEffect를 사용하였고, 콘솔 찍게 했다.

새로고침을 한 직후의 결과 화면이다.

오른쪽 콘솔을 보면, 왜인지 모르겠지만 마운트 되고 -> 언마운트 됐다가 -> 다시 마운트 된다.
그리고 자식 컴포넌트 먼저 마운트 되고 -> 부모 컴포넌트가 마운트 된다. 업데이트 역시 자식부터. Dom tree 가장 아래에 있는 leaf node부터 마운트가 되서 root까지 올라가는 것.

이 상태에서 value에 1 더하기 버튼을 클릭하면 다음과 같이 변한다.

표시된 부분이 콘솔에 새로 나온 내용들이다.

  1. 자식 컴포넌트가 will Update 된다. 이때 보통은 clean-up을 한다.
  2. 자식 컴포넌트의 props가 바뀔 예정이며, 지금은 1이다.
  3. 부모컴포넌트가 업데이트 될 예정이다.
  4. 부모 컴포넌트의 state가 바뀔 예정이며, 지금은 1이다.
  5. 자식이 업데이트 됐다.
  6. 자식의 props가 2로 업데이트 됐다.
  7. 부모가 업데이트 됐다.
  8. 부모의 state가 2로 업데이트 됐다.

마찬가지로 state는 부모에 있지만, render는 자식부터 되고 있다.

정리

  • 보통은 useEffect에 return은 많이 사용하지 않았었는데, 이것도 적절히 사용하면 컴포넌트를 정리하고 중첩된 함수나 이벤트를 쌓는것을 방지 할 수 있을 것 같다.
  • useState로 state값을 정의하고, 초기화하는 것은 컴포넌트가 마운트 될때 한다.
  • 자식이 state로 props를 받아서 사용하면, 마운트 되는 순간 고정되므로 props의 업데이트를 반영하지 못한다.
  • 마운트나 업데이트는 Dom tree의 말단노드 -> 루트 순서로 한다.
  • emit 대신 state를 변경하는 함수를 자식에게 props로 넘겨준다. 데이터는 단방향으로만 흐른다.
profile
(이제부터라도) 기록하려는 프론트엔드 디벨로퍼입니다 XD

0개의 댓글