리액트에서는 prop으로 컴포넌트 바깥으로 데이터를 공유할 수 있습니다.
prop에는 react state뿐 아니라, JS변수, JS함수도 담아 보낼 수 있는데요,
리액트의 설계원칙 중 "prop 불변성" 이란 것이 있습니다.
이번 포스팅에서 가장 중요한 개념인데요, 의미는 다음과 같습니다
부모 컴포넌트에서 전달한 prop 데이터는,
그 값을 받는 자식 컴포넌트에서 수정 할 수 없다.
핵심은, prop을 받아온 자식 컴포넌트에서, 그 값을 직접적으로 바꿀 수 없다는 것입니다.
그런데, 개발을 하다보면 prop으로 받아온 값에 무언가를 추가하거나 제거하는 등 수정해야 하는 상황이 분명 발생할텐데요, 이러한 경우에 어떻게 문제를 해결할 수 있을지 알아보겠습니다.
억지로 문제 상황을 하나 만들어보겠습니다.
정말 억지스럽지만, 내용 이해를 위함이니 넓은 아량으로 이해해주세요.
prop으로 초기 숫자를 받아 버튼의 text로 띄우고,
버튼을 클릭하면 숫자를 하나씩 늘리도록 구현하고 싶다고 가정하겠습니다.
10
47
이렇게 서로 다른 시작값을 가진 버튼이 있고,
각 버튼을 누르면 각각 11과 48이 되도록 구현해보겠습니다.
아래는 잘못 작성된 코드입니다.
//자식 컴포넌트 components/Btn.jsx
const Btn = (props) => {
const handleBtnClick = () => {
props.initialNum += 1;
}
return (
<div>
<h1>버튼을 누르면 숫자가 하나 올라갑니다</h1>
<button onClick={handleBtnClick}>{props.initialNum}</button>
</div>
)
}
export default Btn;
//부모 컴포넌트 pages/Home.jsx
import Btn from "../components/Btn.jsx";
export default function Home() {
<Btn initialNum={10}/>
<Btn initialNum={47}/>
}
코드를 살펴보면,
initialNum
prop에 서로 다른 초기값을 전달하고,
자식 컴포넌트인 Btn
컴포넌트에서 이를 받아와 클릭시 숫자를 하나씩 늘리는 식으로 로직을 구현했습니다.
아이디어 자체는 직관적이지만,
이는 문법상 옳지 않습니다.
리액트의 prop은, 자식 컴포넌트에서 수정할 수 없기 때문입니다.
const handleBtnClick = () => {
props.initialNum += 1;
}
바로 이 부분이 문제인 것이죠.
prop으로 전달받은 값인 initialNum
을 수정하려 하고 있으니,
리액트의 원칙을 위배합니다.
이 부분을 수정해야겠네요.
해결 방법은 허무하리만큼 간단한데요.
리액트에서 prop으로 받은 값을 직접 수정할 수 없으니,
이 값을 새로운 변수에 담아 변경하면 됩니다.
리액트의 state와 setState기능을 활용하면 되겠죠?
Btn컴포넌트를 아래와 같이 수정해봅시다.
//자식 컴포넌트 components/Btn.jsx
import { useState } from "react";
const Btn = (props) => {
const [num, setNum] = useState(props.initialNum);
const handleBtnClick = () => {
setNum((prev) => prev + 1);
}
return (
<div>
<h1>버튼을 누르면 숫자가 하나 올라갑니다</h1>
<button onClick={handleBtnClick}>{num}</button>
</div>
)
}
export default Btn;
이제, 수정된 부분을 하나씩 살펴봅시다
const [num, setNum] = useState(props.initialNum);
먼저, props.initialNum값을 num이라는 state변수의 초기값으로 전달해, 컴포넌트 내의 변수로 만들었습니다.
이렇게되면, props.initialNum과 완전히 같은 값을 갖는 num은 더이상 Btn컴포넌트 외부에서 받은 prop이 아니라, Btn컴포넌트 내의 state가 됩니다. 이제, 자유로운 수정이 가능해졌다는 의미입니다.
const handleBtnClick = () => {
setNum((prev) => prev + 1);
}
이벤트 핸들러도, props.initialNum를 직접 수정하는 것이 아니라,
props.initialNum와 같은 값을 갖지만 prop이 아닌,
Btn 컴포넌트 자체의 state를 수정하는 로직으로 변경되었네요.
Btn컴포넌트 내부의 setNum를 이용해 값을 변경하도록 바꿔주었습니다.
<button onClick={handleBtnClick}>{num}</button>
마지막으로, 버튼의 텍스트에도 props.initialNum
을 직접 넘겨주는 대신 Btn 컴포넌트의 state인 num
을 넘겨주면 되겠습니다.
prop으로 받아온 값은 읽기 전용으로, 자식 컴포넌트에서 prop자체를 직접 수정할 수 없습니다.
그럼에도 prop으로 받아온 값을 수정하는 로직을 구현해야 한다면,
prop값을 직접 다루지 않고, 그 값을 복사하여 컴포넌트 내부의 state로 만들면 됩니다.
감사합니다.