상태?
웹을 렌더하는 데 영향을 미칠 수 있는 값. 이 정의로 보면 useState또한 상태 관리다.
=> 동적인 값이어야 한다.
지역상태
- 특정 컴포넌트 안에서만 관리된다
- 다른 컴포넌트와 데이터를 공유하지 않는다
- 보통 form
컴포넌트 간 상태
- 여러 컴포넌트에서 관리되는 상태
- 일반적으로 prop drilling 방식을 사용한다.
전역 상태
- 프로젝트 전체에 영향을 끼치는 상태
React의 상태관리
상태 관리 라이브러리를 이용하는 방법 외에도 React가 자체적으로 가지고 있는
상태 끌어올리기(Lifting State Up) 을 이용해 React 자체적으로 해결할 수 있다.
상태 끌어올리기
같은 상태가 필요한 여러 컴포넌트들이 공통적으로 가지고 있는 부모 컴포넌트에 상태를 선언한다.
따라서 여러 컴포넌트가 접근해야 하는 상태를 부모 컴포넌트에서 선언한 후 props를 통해 전달하며 상태를 관리할 수 있다.
👎
- 상태를 사용하지 않는 컴포넌트라도 props를 전달해야 한다
- 전달 계층 구조가 깊어진다 (props drilling)
그러나 이 문제 또한 리액트 자체적으로 해결할 수 있다
1. 컴포넌트 합성
React는 강력한 합성 모델을 가지고 있으며, 상속 대신 합성을 사용하여 컴포넌트 간에 코드를 재사용하는 것이 좋습니다
children prop 사용하기
function WelcomeDialog() {
return (
<FancyBorder color="blue"> // ✨ 1
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children} // ✨ 1
</div>
);
}
설명
FancyBorder은 JSX 안에 어떤 내용이 들어올지 모르는 컴포넌트이다.
첫 번째 컴포넌트 WelcomeDialog()에서 FancyBorder을 사용한다
이 때 WelcomeDialog에 있는 FancyBorder 태그 안에 있는 내용이 FancyBorder의 children prop으로 전달된다.
=> 전달된 엘리먼트들이 출력된다.
특수화
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title} // ✨ 1
</h1>
<p className="Dialog-message">
{props.message} // ✨ 2
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome" // ✨ 1
message="Thank you for visiting our spacecraft!" /> // ✨ 2
);
}
기본 구조인 Dialog
Dialog의 특수한 경우인 WelcomeDialog()가 있을때
이같은 구조에서 사용한다.
=> 구체적인 컴포넌트가 일반적인(Dialog) 컴포넌트를 렌더링하고 props를 통해 내용을 구성
2. Context
context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다
- 일반적인 React 애플리케이션에서 데이터는 위에서 아래로 전달되지만
- context를 이용하면 props를 계속 전달하지 않아도 많은 컴포넌트가 이 값을 공유할 수 있다
<theme-context.js>
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext(
themes.dark // 기본값
);
<themed-button.js>
import {ThemeContext} from './theme-context';
class ThemedButton extends React.Component {
render() {
let props = this.props;
let theme = this.context;
return (
<button
{...props}
style={{backgroundColor: theme.background}}
/>
);
}
}
ThemedButton.contextType = ThemeContext;
export default ThemedButton;
관련 내용의 일부만 발췌했다
공식문서에 정리가 잘 되어있으니 공식문서를 읽어보자!
context의 주된 용도는 다양한 레벨에 네스팅된 많은 컴포넌트에게 데이터를 전달하는 것입니다.
context를 사용하면 컴포넌트를 재사용하기가 어려워지므로 꼭 필요할 때 쓰세요
여러 레벨이 걸쳐 props 넘기는 걸 대체하는 데에 context보다 컴포넌트 합성이 더 간단한 해결책일 수도 있습니다.
useReducer과 useContext를 사용하면 생성된 상태와 디스패치 함수를 모든 최상위 컴포넌트에 전달한다.
따라서 컴포넌트 트리에 모든 내용을 전달하지 않고도 상태와 디스패치 함수를 사용할 수 있다.
그러나
컴포넌트 합성과 context를 통해 상태를 전달할 수는 있지만 React 공식문서에서는
Facebook에서는 수천 개의 React 컴포넌트를 사용하지만, 컴포넌트를 상속 계층 구조로 작성을 권장할만한 사례를 아직 찾지 못했습니다.
라고 말한다.
권장할만한 사례를 아직 찾지 못했다는 것이지 React에서 자체적인 상태관리가 불가능하다는 것은 아니다.
따라서 자체적 관리가 아닌 라이브러리를 도입해 상태를 관리할 때는 명확한 이유가 필요하다.
상태관리 라이브러리의 필요성
상태가 복잡해졌을 때. 전역 상태가 생겼을 때 필요하다
🤔 Context API로는?
이용할 수도 있지만 Context API는 값이 변할 때 Context를 구독하는 모든 컴포넌트들에게서 리렌더링이 발생한다.
이를 제어하기 위해 useReducer이나 momoization을 사용할 수 있지만 번거롭다
상태관리 라이브러리들
Redux
React 기반의 상태 관리 라이브러리
React의 공식 상태 관리 라이브러리는 아님.
< 작동 방식 >
출처 : https://dev.to/workshub/state-management-battle-in-react-2021-hooks-redux-and-recoil-2am0
특징
UI에서 사용자의 엑션에 반응하며 상태 업데이트를 처리한다
모든 프레임워크에서 독립 실행형 관리 툴로 사용할 수 있다.
👍
- 상태를 한 곳에서 관리하고 변경사항이 예측가능, 추적가능해진다.
상태가 불변하며 변하지 않는다
=> 무한한 실행 취소 등의 작업을 구현할 수 있다
이전 상태를 오가면서 결과를 확인할 수 있다. - 변경 사항을 쉽게 파악할 수 있다
- React-Redux는 대규모 사용자 커뮤니티가 구축되어 있다.
- 성능 최적화를 보장하여 필요할 때만 재렌더링한다.
- Rocal storage에 상태를 유지하고 복원할 수 있다.
- SSR이 가능
서버 요청에 대한 응답과 함께 앱의 상태를 서버로 전송 - 유지보수성
컴포넌트 트리에서 비즈니스 로직 분리가 가능하다 - 디버깅이 쉽다
개발자도구가 많다
👎
- 보일러플레이트 코드가 커진다 => 불필요하게 어플리케이션이 커진다
=> 구성을 잘 해야 한다
Recoil
React의 공식 상태 관리 라이브러리는 아님.
하지만 React를 만든 페이스북 개발팀에 의해 제작, 출시되었다.
< 작동 방식 : atoms >
👍
- 쉬운 러닝 커브 : React useState의 글로벌 버전 같음
- Boilerplate-free API
- 심플하다
- atom이라는 상태 저장 공간을 두어 상태를 쉽게 참조할 수 있다.
- 상태를 기반으로 하는 파생 데이터를 계산할 수 있는 selector 함수를 이용하여 효율적으로 계산할 수 있다 : 쓸모없는 상태의 보존 방지
- 간편한 비동기 처리 : redux에서는 추가적인 api였던 비동기 처리나 캐싱을 자체적으로 지원한다
- 렌더링 최적화
🙇♀️
https://ko.reactjs.org/docs/composition-vs-inheritance.html#gatsby-focus-wrapper
https://ko.reactjs.org/docs/context.html
'✍ 공부 > React' 카테고리의 다른 글
[ 🐱💻 강의 노트 ] React 완벽 가이드 강의 정리 섹션 29 | 리엑트 요약 및 핵심 기능 (0) | 2022.05.31 |
---|---|
[ 🐱💻 강의 노트 ] React 완벽 가이드 강의 정리 (0) | 2022.05.26 |
[ TIL ] React.memo() 적절하게 사용하기 (0) | 2022.04.13 |
[ TIL ] React.PureComponent란 (0) | 2022.04.06 |
[ React Hook Form ] react에서 form 쉽게 이용하기 (+TypeScript) (0) | 2022.04.01 |