동일한 데이터를 여러 컴포넌트에서 사용하고, 변경됐을 때 바로 반영되어야 한다면 각 컴포넌트가 state를 가지고 있는 것이 아니라 공통된 상위 컴포넌트에서 state
를 처리하고, 하위 컴포넌트에서는 상위로 전달하는 방법을 사용해야 한다. React에서는 이 방법을 State 끌어올리기라고 한다.
state 끌어올리기란 상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행하는 것을 말한다.
상태 변경 함수(콜백 함수)를 자식에게 props로 전달하고, 자식에서 해당 함수를 호출하면 부모의 state가 변동되는 원리이다.
즉, state의 변화는 컴포넌트 내부에서만 이루어지지만, 이러한 변화를 일으키는 호출을 외부(자식 컴포넌트)에서 함으로서 마치 외부에서 내부 state를 변화시키는 것 같은 효과를 가진다.
하위 컴포넌트인 <ChildComponent>
는 마치 고차 함수가 인자로 받은 함수를 실행하듯, props
로 전달받은 함수를 컴포넌트 내에서 실행할 수 있게 된다.
'상태 변경 함수'는 버튼이 클릭할 때 실행되야 하므로, 해당 부분에 콜백 함수를 실행한다.
// 부모(상위) 컴포넌트
function ParentComponent() {
const [value, setValue] = useState("부모 컴포넌트의 state 값");
const handleChangeValue = () => {
setValue("변경된 값");
};
return (
<div>
<div>{`끌어올리기 결과값: ${value}`}</div>
<ChildComponent handleButtonClick={handleChangeValue} />
</div>
);
}
// 자식(하위) 컴포넌트
function ChildComponent({ handleButtonClick }) {
const handleClick = () => {
handleButtonClick();
};
return <button onClick={handleClick}>값 변경</button>;
}
// 부모(상위) 컴포넌트
function ParentComponent() {
const [value, setValue] = useState("부모 컴포넌트의 state 값");
const handleChangeValue = (newValue) => {
setValue(newValue);
};
return (
<div>
<div>{`끌어올리기 결과값: ${value}`}</div>
<ChildComponent handleButtonClick={handleChangeValue} />
</div>
);
}
// 자식(하위) 컴포넌트
function ChildComponent({ handleButtonClick }) {
const handleClick = () => {
handleButtonClick("변경된 값");
};
return <button onClick={handleClick}>값 변경</button>;
}
// 부모(상위) 컴포넌트
export default function Parent() {
const [count, setCount] = useState(0);
function onClickCounter() {
setCount(count + 1);
}
return (
<>
<FirstChild count={count} onClickCounter={onClickCounter} />
<div>===========</div>
<SecondChild count={count} onClickCounter={onClickCounter} />
</>
);
}
// 자식(하위) 컴포넌트1
export default function FirstChild({ count, onClickCounter }) {
return (
<div>
<div>자식1 카운트: {count}</div>
<button onClick={onClickCounter}>카운트올리기</button>
</div>
);
}
// 자식(하위) 컴포넌트2
export default function SecondChild({ count, onClickCounter }) {
return (
<div>
<div>자식2 카운트: {count}</div>
<button onClick={onClickCounter}>카운트올리기</button>
</div>
);
}