React로 개발하다보면 상태관리에 대한 고민을 안할 수가 없다. 나 또한, 상태관리에 대해 고민하던 중 회사에서 redux를 적용했는데 redux 까지는 여차저차 괜찮았으나, 추가로 redux-toolkit, redux-saga 를 적용하려고 보니, 러닝커브가 높아서 막막했다. (더 편하게 개발하라고 나온 것들이지만...) 그러던 중 작년 5월즈음에 페이스북에서 recoil 을 발표했고, 고민없이 recoil 을 적용해봤는데 React hooks 개념이고 초기셋팅도 엄청 간단해서 recoil 로 넘어와버렸다...
아직 정식 major 버전은 릴리즈를 못한 것 같다. 하지만 minor 버전은 계속 업그레이드 하고 있고, 곧 정식 릴리즈가 되지 않을까 한다.
일단 먼저 recoil 을 테스트하기 위한 실습 프로젝트를 CRA를 통해 생성한다.
npx create-react-app react-recoil-base
cd react-recoil-base
리코일 패키지를 설치한다.
yarn add recoil
※ 아직은 별다른 확장형 패키지는 없는 것 같다.
프로젝트의 엔트리 파일 (./index.js) 에서 RecoilRoot로 감싸준다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { RecoilRoot } from 'recoil';
ReactDOM.render(
<RecoilRoot>
<App/>
</RecoilRoot>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
※ RecoilRoot 로 감싸는 컴포넌트(여기서는 <App/>) 부터 리코일 객체(atom)를 사용할 수 있음을 유의한다.
초기 셋팅 자체는 상당히 간단하다. (redux 처럼 리듀서를 만들고 store 를 생성한 뒤 공급자에 넣어줄 필요가 없다.)
이 후, atom 객체를 생성한다. (./src/states/counter.js)
import { atom, selector } from 'recoil';
export const counterState = atom({
key: 'counterState',
default: 0
});
export const counterLabelSelector = selector({
key: 'counterLabelSelector',
get: ({ get }) => {
const counter = get(counterState);
return `현재 카운터는 ${counter} 입니다.`;
}
});
※ atom 은 redux 의 store 와 유사한 개념으로, 상태의 단위 이다. React 에서 사용하는 state 와 같은 개념으로 atom 의 값이 변경되면, atom 을 구독하고 있는 모든 컴포넌트들이 리렌더링되고 변경된 atom 의 state 값을 사용하게 된다.
- atom 객체는 key 와 default 두가지의 필수 값이 존재하는데 key 는 리덕스의 action type?? 과 비슷한 느낌이고, default 는 atom 초기 값 이다. (key 는 보통 atom 객체의 변수명과 같게 지정했다.) 또한, key 는 유일해야하므로 중복되면 안된다.
- selector 객체는 read-only 한 RecoilValueReadOnly 객체로서 보통 atom 의 상태 값을 기반으로 새로운 값을 리턴할 때 사용한다. 물론 selector 는 get 을 구독하는 atom 객체가 변화하면 같이 변경된다.
- Line 3~6 : atom 객체 생성
- Line 8~14 : selector 객체 생성
- Line 11 : selector 가 가져온 atom 객체
recoil 을 사용해서 상태값을 변경하는 컴포넌트를 생성한다. (./src/components/Counter.jsx)
import React, { useState } from 'react';
import { useRecoilState } from 'recoil';
import { counterState } from '../states/counter';
const Counter = () => {
const [counter, setCounter] = useRecoilState(counterState);
const [state, setState] = useState({});
const handleChange = (evt) => {
setState({
...state,
[evt.target.name]: Number(evt.target.value)
})
};
const handleClickPlus = () => {
setCounter(counter + (state?.plus ?? 1));
};
const handleClickMinus = () => {
setCounter(counter - (state?.minus ?? 1));
};
return (
<div>
<input type="text" name="plus" value={state?.plus} onChange={handleChange} />
<button onClick={handleClickPlus}>+</button>
<input type="text" name="minus" value={state?.minus} onChange={handleChange} />
<button onClick={handleClickMinus}>-</button>
</div>
);
}
export default Counter;
- Line 6 : useRecoilState 를 통해 counterState atom 객체를 구독한다. (방식은 useState 와 거의 동일)
- Line 16~21 : atom 객체의 상태값을 변경하는 이벤트함수이다. (redux 의 action 함수와 유사 - dispatch 같은거 필요 없음!)
변경된 atom 객체를 렌더링 하는 소스를 작성한다. (./src/App.js)
import React from 'react';
import { useRecoilValue } from 'recoil';
import { counterState, counterLabelSelector } from './states/counter';
import Counter from './components/Counter';
function App() {
const counter = useRecoilValue(counterState);
const counterLabel = useRecoilValue(counterLabelSelector);
return (
<div>
<Counter/>
<div>counter : {counter}</div>
<div>counterLabel : {counterLabel}</div>
</div>
);
}
export default App;
- Line 7~8 : useRecoilValue(상태 값만을 리턴) 을 통해 atom 및 selector 객체를 구독한다.
위 화면으로 recoil 을 통한 state 변화를 확인할 수 있다.
recoil 에서는 위에서 사용한 hooks 말고도 더 많은 기능을 제공한다.
필요에 따라 [공식 문서]를 참고하여 적용하면 될 것으로 보인다.
위 예제에 대한 샘플소스는 깃허브에 올려놓았습니다.
직접 동작해보고, atom 및 selector 객체를 만들어보면서 실습해보면 이해가 쉬울 것 입니다.
https://github.com/sbjang123456/react-recoil-base
'React' 카테고리의 다른 글
React userEvent test 코드 작성시 Error: Expected key descriptor but found "" in "" (0) | 2022.05.13 |
---|---|
React recoil Asynchronous (비동기처리) (0) | 2021.06.11 |
React + Redux 초기 셋팅 및 테스트 (1) | 2021.06.09 |
React + Openlayers 구성 ( By React Context ) (0) | 2021.05.31 |
React(+material-ui) draggable + resizing modal 컴포넌트 (0) | 2021.05.22 |