Shared State
리액트로 개발을 하다 보면, 하나의 데이터를 여러 개의 컴포넌트에서 표현해야 하는 경우가 생긴다.
이 경우, 각 컴포넌트의 state에서 데이터를 각각 보관하는 것이 아니라
가장 가까운 공통된 부모 컴포넌트의 state를 공유해 사용하는 것이 더 효율적이다.
이를 Shared State라고 하며, state에 있는 데이터를 여러 개의 하위 컴포넌트에서 공통적으로 사용하는 경우를 말한다.
자식 컴포넌트들이 각각 값을 가지고 있을 필요가 없고, 부모 컴포넌트의 state에 연산(*2)를 해주면 된다.
하위 컴포넌트에서 State 공유하기
1. 섭씨온도를 props로 받아 물이 끓는지 안 끓는지 문자열로 출력해주는 컴포넌트를 만든다.
function BoilingVerdict(props) {
if(props.celsius>=100) {
return <p>물이 끓습니다.</p>
}
return <p>물이 끓지 않습니다.</p>
}
2. 이 컴포넌트를 사용하는 부모 컴포넌트를 만든다.
function Calculator(props) {
const [temperature, setTemperature] = useState('');
const handleChange = (event) => {
setTemperature(event.target.value);
}
return (
<fieldset>
<legend>섭씨 온도를 입력하세요. :</legend>
<input
value={temperature}
onChange={handleChange}
/>
<BoilingVerdict
celsius={parseFloat(temperature)}
/>
</fieldset>
)
}
3. Calculator 안에 온도를 입력하는 부분을 별도로 추출한다.
⏩ 섭씨온도와 화씨온도를 각각 따로 입력받아, 재사용 가능한 컴포넌트를 만들기 위해
const scaleName = {
c: '섭씨',
f: '화씨'
};
function TemperatureInput(props) {
const [temperature, setTemperature] = useState("");
const handleChange = (event) => {
setTemperature(event.target.value);
}
return(
<fieldset>
<legend>
온도를 입력해주세요(단위:{scaleName[props.scale]});
</legend>
<input value={temperature} onChange={handleChange} />
</fieldset>
)
}
Calculator 수정
function Calculator(props) {
return (
<div>
<Temperature scale="c"/>
<Temperature scale="f"/>
</div>
)
}
이 경우, 사용자가 입력하는 온도가 TemperatureInput의 state에 저장되므로,
섭씨온도값과 화씨온도값을 따로 입력받으면 두 개의 값이 다를 수 있다.
(개별로 존재하는 2개의 컴포넌트에 대해, 입력값이 각각의 state에 따로 저장된다.)
이를 해결하기 위해서는 값을 동기화시켜 주어야 한다.
4. 온도 변환 함수 작성하기
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32
}
function tryConvert(temperature, convert) { // (온도, 변환하는 함수)
const input = parseFloat(temperature);
if(Number.isNaN(input)) { // 예외처리
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
tryConvert('abc', toCelsius); // empty string 리턴
tryConvert('10.22', toFahrenheit); //'50.396' 리턴
5. Shared State 적용하기
하위 컴포넌트의 state를 공통된 부모 컴포넌트의 state로 끌어올린다. ( Lifting State Up )
function TemperatureInput(props) {
...
return(
...
// 변경전 : <input value={temperature} onChange={handleChange} />
<input value={props.temperature} onChange={handleChange} />
...
)
}
온도값을 컴포넌트의 state에서 가져오는 것이 아닌, props를 통해서 가져온다. (상위 컴포넌트로부터 전달받는다.)
컴포넌트의 state를 사용하지 않기 때문에, 입력값이 변경되었을 때 상위 컴포넌트로 변경된 값을 전달해야 한다.
const handleChange = (event) => {
// 변경 전 : setTemperatureChange(event.target.value);
props.onTemperatureChange(event.target.value);
}
사용자가 온도값을 변경할 때마다, onTemperature 함수를 통해 변경된 온도값이 상위 컴포넌트로 전달된다.
이 때 onTemperature 함수는 상위 컴포넌트에 있어 props로 전달 받는다.
최종적으로 완성된 TemperatureInput 컴포넌트의 모습은 아래와 같다.
function TemperatureInput(props) {
const handleChange = (event) => {
props.onTemperatureChange(event.target.value);
}
return (
<fieldset>
<legend>
온도를 입력해주세요. (단위 : {scaleNames[props.scale]})
</legend>
<input value={props.temperature} onChange={handleChange} />
</fieldset>
)
}
6. Calculator 컴포넌트 변경하기
변경된 TemperatureInput 컴포넌트에 맞춰, Calculator 컴포넌트를 변경한다.
function Calculator(props) {
const [temperature, setTemperature] = useState("");
const [scale, setScale] = useState("c");
const handleCelsiusChange = (temperature) => {
setTemperature(temperature);
setScale('c');
}
const handleFahrenheitChange = (temperature) => {
setTemperature(temperature);
setScale('f');
}
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={handleFahrenheitChange}
/>
<BoilingVerdict
celsius={parseFloat(celsius)}
/>
</div>
)
}
🚀 참고
- 처음 만난 리액트(React) , 소플 - 섹션12. Lifting State Up
'React' 카테고리의 다른 글
[React] 미니 블로그 프론트단 작성, 빌드 - router, route, useNavigate() (0) | 2023.01.30 |
---|---|
[React] styled-components 설치 및 사용, 스타일 확장 (0) | 2023.01.12 |
[React] Context API - useContext() (0) | 2023.01.07 |
[React] Context 개념 및 특징 (0) | 2023.01.06 |
[React] Composition VS. Inheritance - Containment, Specialization (0) | 2023.01.06 |
댓글