어플리케이션의 화면에 영향을 끼치는 자바스크립트 객체
보통은 이를 통칭해서 "변화하는 데이터"라고 일컫는다.
상태 중에서도 전역상태란 프로젝트 전체에 영향을 끼치는 상태를 의미한다.
일반적으로 리액트에서 데이터는 부모로부터 props를 통해 전달된다. 그러나 컴포넌트를 나누다보면 상위 컴포넌트의 state를 props를 통해 전달하고자 하는 컴포넌트로 전달하기 위해 그 사이는 props를 전달하는 용도로만 쓰이는 props drilling 현상이 나타난다.
이와 같이 props로만 계속해서 자식 컴포넌트에 데이터를 전달한다면 코드가 지저분해지고, 유지보수 또한 힘들어진다.
또한 state 변경시 props 전달 과정에서 불필요하게 관여된 컴포넌트들도 리렌더링이 발생하므로 웹성능에 악영향을 끼칠 수 있다.
이를 해결하기 위해서 우리는 전역상태를 관리해주는 도구를 사용해서 상태관리를 해주어야 한다.
(전역상태 관리 도구로는 대표적으로 Context API, Recoil, Redux 등이 있다.)
페이스북에서 만든 React 프로젝트를 위한 전역 상태관리 라이브러리
$ yarn add recoil
리코일 state를 사용하는 컴포넌트들은 <RecoilRoot>
를 필요로한다. 루트 컴포넌트가 RecoilRoot
를 넣기 가장 좋은 장소이다.
App.js
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';
function App() {
return (
<RecoilRoot>
<Children />
</RecoilRoot>
);
}
컴포넌트끼리 공유 가능한 가장 작은 단위의 state
아톰은 상태의 단위이며 어떠한 컴포넌트에서나 읽고 쓸 수 있다. 아톰 값을 읽는 컴포넌트들은 암묵적으로 그 아톰을 구독한다. 아톰이 업데이트되면, 해당 아톰을 구독하고 있던 모든 컴포넌트들이 새로운 값으로 리렌더링된다.
또, 여러 컴포넌트에서 같은 아톰을 구독하고 있으면 그 컴포넌트들은 상태를 동일하게 공유한다.
import { atom } from 'recoil';
const textState = atom({
key: 'textState', // unique ID
default: '', // initial value
});
아톰마다 고유한 값 key를 가지며, default에 기본값(초기값)을 할당해준다.
function CharacterCounter() {
return (
<div>
<TextInput />
<CharacterCount />
</div>
);
}
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
useRecoilState
를 이용하여 state를 전역으로 받아올 수 있고 변경할 수 있다. (useState와 유사)
Atom 또는 다른 Selector로 구성한 순수함수
셀렉터는 파생된 상태(derived state)의 일부를 나타낸다.
파생된 상태란 상태의 변화로, 주어진 상태를 변경시키는 순수함수에 의해 전달된 상태의 결과물이라고 할 수 있다.
구독 중인 아톰 또는 셀렉터가 업데이트되면 셀렉터도 업데이트 된다.
import { atom, selector } from 'recoil';
export const sampleState = atom({
key: "sampleState",
default: 0,
});
export const sampleSelector = selector({
key: "sampleSelector",
get: ({ get }) => get(sampleState) * 2,
set: ({ set }, newValue) => set(sampleState, newValue / 2),
});
get
을 이용하여 다른 아톰도 구독할 수 있다. -> 즉, 해당 아톰이 변하면 셀렉터도 변한다.
get
을 통해 전처리한 atom의 값을 받아오고, set
을 통해 atom값을 후처리할 수 있다.
셀렉터에서 getter, setter를 사용해 전처리 및 후처리한 atom을 사용할 수 있다.
사용 방법은 atom과 마찬가지로 useRecoilState
를 사용한다.
useState와 유사한 기능이다. 그러나 선언을 하는 게 아닌 전역적으로 설정된 atom을 가져오는 역할이다.
const [sample,setSample] = useRecoilState(sampleState);
아톰을 조회할 때만 사용한다.
const sample = useRecoilValue(atom);
값을 불러오지 않고 상태만을 설정할 때 사용한다. 아톰을 변경시킬 수 있다.
const setSample = useSetRecoilState(atom);
아톰 값을 디폴트값(초기값)으로 변경시킨다.
Atom.js
import { atom, selector } from 'recoil';
const tempFahrenheit = atom({
key: 'tempFahrenheit',
default: 32,
});
const tempCelsius = selector({
key: 'tempCelsius',
get: ({get}) => ((get(tempFahrenheit) - 32) * 5) / 9,
set: ({set}, newValue) => set(tempFahrenheit, (newValue * 9) / 5 + 32),
});
export {tempFahrenheit, tempCelsius}
TempCelsius.js
import {atom, selector, useRecoilState, useRecoilValue} from 'recoil';
import {tempFahrenheit, tempCelsius} from "./Atoms.js"
function TempCelsius() {
const [tempC, setTempC] = useRecoilState(tempCelsius);
const [tempF, setTempF] = useRecoilState(tempFahrenheit);
const originTempC = useRecoilValue(tempCelsius);
const originTempF = useRecoilValue(tempFahrenheit);
const addTenCelsius = () => setTempC(tempC + 10);
const addTenFahrenheit = () => setTempF(tempF + 10);
return (
<div>
Temp (Celsius): {tempC}
<br />
Temp (Fahrenheit): {tempF}
<br />
<button onClick={addTenCelsius}>Add 10 Celsius</button>
<br />
<button onClick={addTenFahrenheit}>Add 10 Fahrenheit</button>
<br />
<p>{`originC : ${originTempC} / originB : ${originTempF}`}</p>
</div>
);
}
export default TempCelsius;
결과