React 상태 올리기 (React Lifting State Up)

같은 state를 공유하는 두개의 컴퍼넌트

상태 올리기라는건 언제 쓰는걸 까요?
그 이유를 알기 위해서 State를 공유하는 두개의 컴퍼넌트에 대해서 알아보겠습니다.

React에서는 단방향 데이터 흐름이라는 원칙에 따라, 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 혹은 타입이 무엇인지만 알 수 있다. 데이터가 state로부터 왔는지, 하드코딩으로 입력한 내용인지는 알지 못한다..

단방향 데이터 흐름인 이유?
--> 단방향 데이터 흐름을 사용하지 않으면 그 때 그 때 기능 변경 사항에 대해서 코드를 계속 작성해야 된다.
--> 코드의 흐름을 알기 쉽다, 복잡하지 않게 된다.
--> 컴포넌트의 중요한 상태만 잘 관리하면
--> 아래에서 UI가 자동으로 변경된다(다소 중앙집권적. 중요데이터 하나가 변경되었을 때, 많은 컴포넌트에 영향을 주는 것을 굳이 작성하지 않아도 된다)
--> 이것이 제일 편한 방법...

하위 컴포넌트에서의 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것에 대하여 React가 제시하는 해결책은 다음과 같다.

상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다.

이것이 바로 "상태끌어올리기"이다!! 단방향 데이터 흐름의 원칙에 부합하는 해결방법이다.

그러면 상태끌어올리기를 하기 위해서는 어떻게 해야할까라는 의문이 들 것이다...! 바로 js의 callback을 이용하면 된다!

Callback 다시보기
콜백(callback)은 다른 함수(고차함수)의 인자로 전달되는 함수를 의미한다.
다시 설명하자면 callback은 비동기 프로그램때문에 나온 함수라고 생각하면 된다. 컴퓨터는 한 번에 한가지 일만 처리한다. 그러니깐 작업을 요청한 이후에 다음 작업을 진행할 수 없다. 만약 통신 과정이 포함되어있고, 데이터를 보낸 이후에 답이 없다면 멈춰버리기 때문에 이러한 점을 해결하고자 비동기 프로그램을 위해서 나온 함수가 콜백함수이다. 즉, 콜백함수는 비동기 이벤트가 실행되고 난 이후에 실행될 함수를 의미한다.

// 고차함수
function each(array, iterator) {
  for(let i = 0; i < array.length; i++) {
    let element = array[i]
    iterator(element, i, array)
  }
}

// 콜백 함수
function printElement(element) {
  console.log(element)
}

each(['hello', 'world'], printElement);

이제 React의 해결책에서 알아보자!
React에선 다음과 같이 말했다.

상위 컴포넌트의 "상태를 변경하는 함수" 그자체를 하위 컴포넌트로 전달하고, 이 함수를 하위컴포넌트가 실행한다.

여기서 말하는 상태를 변경하는 함수는 바로 handleChangeValue이다. 전달은 props를 이용하면 된다!

function ParentComponent() {
  const [value, setValue] = useState("날 바꿔줘!");

  const handleChangeValue = () => {
    setValue("보여줄게 완전히 달라진 값");
  };

  return (
    <div>
      <div>값은 {value} 입니다</div>
      <ChildComponent handleButtonClick={handleChangeValue}  />
    </div>
  );
}

childComponent는 마치 고차함수가 인자로 받은 함수를 실행하듯, props로 전달받은 함수를 컴포넌트 내에서 실행할 수 있게 된다. "상태 변경 함수"는 버튼이 클릭할 때 실행되기를 원하기 때문에 해당 부분에 콜백함수를 실행시켜주면 된다...!

function ChildComponent({ handleButtonClick }) {
  const handleClick = () => {
    // Q. 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
    // A. 인자로 받은 상태 변경 함수를 실행하자!

    handleButtonClick()
  }

  return (
    <button onClick={handleClick}>값 변경</button>
  )
}

또한 필요에 따라 설정할 값을 콜백 함수의 인자로 넘길 수도 있다!

function ParentComponent() {
  const [value, setValue] = useState("날 바꿔줘!");

  const handleChangeValue = (newValue) => {
    setValue(newValue);
  };

  // ...생략...
}

function ChildComponent({ handleButtonClick }) {
  const handleClick = () => {
    handleButtonClick('넘겨줄게 자식이 원하는 값')
  }

  return (
    <button onClick={handleClick}>값 변경</button>
  )
}

좋은 웹페이지 즐겨찾기