상태 올리기라는건 언제 쓰는걸 까요?
그 이유를 알기 위해서 State를 공유하는 두개의 컴퍼넌트에 대해서 알아보겠습니다.
React에서는 단방향 데이터 흐름이라는 원칙에 따라, 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 혹은 타입이 무엇인지만 알 수 있다. 데이터가 state로부터 왔는지, 하드코딩으로 입력한 내용인지는 알지 못한다..
단방향 데이터 흐름인 이유?
--> 단방향 데이터 흐름을 사용하지 않으면 그 때 그 때 기능 변경 사항에 대해서 코드를 계속 작성해야 된다.
--> 코드의 흐름을 알기 쉽다, 복잡하지 않게 된다.
--> 컴포넌트의 중요한 상태만 잘 관리하면
--> 아래에서 UI가 자동으로 변경된다(다소 중앙집권적. 중요데이터 하나가 변경되었을 때, 많은 컴포넌트에 영향을 주는 것을 굳이 작성하지 않아도 된다)
--> 이것이 제일 편한 방법...
상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다.
이것이 바로 "상태끌어올리기"이다!! 단방향 데이터 흐름의 원칙에 부합하는 해결방법이다.
그러면 상태끌어올리기를 하기 위해서는 어떻게 해야할까라는 의문이 들 것이다...! 바로 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>
)
}