일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- EventListener
- react
- 상태
- deep
- 개발
- DoM
- axios
- memory
- 프로그래머스
- virtual Dom
- API
- 백준
- javascript
- State
- programmers
- Hook
- LeetCode
- 프로젝트
- 가상 DOM
- 상태 끌어올리기
- 요청
- Java
- 유용한 사이트
- BOJ
- 원리
- Dive
- 꿀팁
- Today
- Total
탄탄한 기본기!
[React] Lifting State Up - 상태 끌어 올리기 본문
동일한 데이터를 여러 컴포넌트에서 공유하고 싶을 때,
상태를 끌어올려서 반영해줄 수 있다
Lifting State Up – React
A JavaScript library for building user interfaces
reactjs.org
Source of truth
리액트 공식 문서에서는 변경 사항들을 관리할 때에 단 하나의 "source of truth"를 두어야 한다고 설명하고 있다.
보통 리액트 컴포넌트를 설계할 때, 먼저 해당 컴포넌트에 state를 저장하여 관리하게 한다. 하지만 애플리케이션이 커지고, 로직이 변화하거나 하는 등의 과정을 거치면서 다른 컴포넌트도 역시 같은 상태를 공유해 함께 반영되어야 할 상황이 충분히 생길 수 있다. 이때, 여러 가지 방식으로 상태들을 "동기화"할 수 있겠지만, 리액트는 여러 방법들 중 "top-down data flow" 방식을 활용해 해결할 것을 추천(?)하고 있다.
리액트 공식 문서에서 들고 있는 예시에 따르면, 물의 끓는 점을 섭씨온도와 화씨온도로 표현하고자 할 때 섭씨온도와 화씨온도가 "온도"라는 같은 상태를 공유하고 그 온도를 섭씨, 또는 화씨로 적절히 변환해 렌더링 해주는 컴포넌트가 있다고 가정하겠다. 섭씨온도를 표현하는 컴포넌트와 화씨온도를 표현하는 컴포넌트는 각각 온도를 관리할 것이 아니라 이 둘 컴포넌트의 부모 컴포넌트(컨테이너)에서 상태를 가지고 이 상태를 변경하는 함수를 각 컴포넌트에게 넘겨주어 변화가 있을 때마다 setState를 호출하게 하는 방식으로 관리를 해주는 것이 바로 상태를 끌어올리는 방법인 것이다.
컴포넌트 개요
컴포넌트를 구성할 때, type check와 defaultProps 등 세세한 컴포넌트 설계를 하지는 않고 간략하게만 구성해보았다.
// BoilingVerdict.js
function BoilingVerdict({ temperature, type }) {
const celsius = type === 'c' ? temperature : toCelsius(temperature);
return <h2>{celsius >= 100 ? '부글부글 끓음 !!' : ' ... '}</h2>;
}
// TemperatureInput.js
class TemperatureInput extends Component {
state = {
temperature: 0,
type: this.props.type,
};
handleChange(e) {
this.setState({
temperature: e.target.value,
});
}
render() {
const { temperature, type } = this.state;
return (
<div className={type === 'c' ? 'celsius' : 'fahrenheit'}>
<label htmlFor="temperature">
{type === 'c' ? '섭씨: ' : '화씨: '}
</label>
<input
id="temperature"
name="temperature"
value={temperature}
onChange={this.handleChange.bind(this)}
/>
</div>
);
}
}
이렇게 간단한 컴포넌트 두 개가 있다고 가정해보겠다. 그리고 이 두 컴포넌트를 포함하는 App 컨테이너는 다음과 같다.
function App() {
return (
<div className="App">
<BoilingVerdict temperature={100} type="c" />
<TemperatureInput type="c" />
<TemperatureInput type="f" />
</div>
);
}
그럼 위와 같은 상황이 있다. 위에서부터 차례대로
- 물이 끓는지 아닌지 판단하여 출력해주는 컴포넌트
- 온도를 입력받는 컴포넌트 (섭씨)
- 온도를 입력받는 컴포넌트 (화씨)
그리고 온도를 입력받는 컴포넌트는 각각 "temperature"와 "type"을 상태로 각각 가지고 있다.
문제점
위와 같은 구성의 문제점은, 각각의 컴포넌트들이 스스로 상태를 가지고 있기 때문에 서로 "동기화"를 할 수 없다는 점이다. 즉, 섭씨 온도를 수정하더라도 물이 끓는지 안 끓는지 표현하는 컴포넌트에게 props를 전달할 수 없고, 화씨 컴포넌트에게 역시 변경된 온도를 전달해줄 수 없다.
따라서 이 때 상태를 끌어올려 부모에게 상태를 저장한 뒤 이를 "top-down data flow"를 활용해 상태를 관리해주어야 한다.
해결
먼저 이 세가지 컴포넌트들을 모두 관리하는 컴포넌트인 BoilCheck라는 클래스 컴포넌트를 생성해 이 컴포넌트에서 관리해준다. 이때 temperature라는 상태와 type이라는 상태 총 2가지 상태를 관리해주는데, 이 상태들을 업데이트해줄 수 있는 함수를 선언해 Input 컴포넌트에게 전달해준 후 setState를 할 수 있게 해 준다.
이렇게 되면, 결과적으로 부모 컴포넌트인 BoilCheck의 상태가 하위 컴포넌트들의 입력에 반응하여 계속 변화할 것이고, 그 하위의 모든 컴포넌트들은 이 상태에 의존하기 때문에 결국 전체적으로 동기화되어 동작할 것이다.
// BoilCheck.js
class BoilCheck extends Component {
state = {
temperature: 0,
type: 'c',
};
handleOnChange(e, type) {
this.setState({
temperature: e.target.value,
type: type,
});
}
render() {
const { type, temperature } = this.state;
return (
<div>
<BoilingVerdict temperature={temperature} type={type} />
<TemperatureInput
type="c"
temperatureType={type}
temperature={temperature}
onChange={this.handleOnChange.bind(this)}
/>
<TemperatureInput
type="f"
temperatureType={type}
temperature={temperature}
onChange={this.handleOnChange.bind(this)}
/>
</div>
);
}
}
결과
처음 "상태 끌어올리기"라는 개념을 들었을 때는 React createPortal과 같은 API가 있는 줄 알았다. 하지만 그런 API가 따로 존재하는 것이 아니라 자바스크립트가 콜백 함수를 이용하여 비동기적으로 어떤 일을 수행하는 것처럼, 상태 관리를 비동기적으로 하위 컴포넌트들이 관리할 수 있도록 제어권을 넘겨주는 방식으로 동작하도록 하는 개념 같은 것이었다.
다시 한번 느끼는 것이지만... React는 공식문서가 정말 정리가 잘 되어있는 것 같다.
만약 Javscript의 기본적인 문법들을 탄탄하게 알고 있는 상태에서 React를 공부하고 싶다면, 책을 사서 공부하는 것보다 공식문서로 공부하는 게 훨씬 좋을 것 같다...
https://reactjs.org/docs/lifting-state-up.html#lessons-learned
'개인 공부 > React' 카테고리의 다른 글
React Router production 경로 라우팅 문제 (0) | 2022.03.03 |
---|---|
[React] hook 내부 동작 원리 (feat. VirtualDOM) (0) | 2021.12.05 |
[간단 프로젝트] Break-Boredness (1) | 2021.08.25 |