Recoli 이란 ?
recoli은 페이스북에서 개발한 새로운 상태관리 라이브러리입니다. React에서는 자체적으로 상태관리를 할 수 있는
Hooks이나 ContextAPI를 제공합니다. 그러나 내장 상태관리 프로그램은 다음과 같은 몇가지 단점이 있습니다.
- 컴포넌트의 상태는 공통된 상위요소에서 공유될수 있지만, 이 과정에서 거대한 트리가 다시 렌더링 되는 효과를 야기함
- Context는 단일 값만 저장할수 있고, 여러 값들의 집합을 담을 수 는 없다.
- 이 두가지 특성이 트리의 최상단(state가 존재하는 곳) 부터 트리의 잎(state가 사용되는 곳) 까지의 코드 분할을 어렵게 만듦
그래서 사람든은 보통 이러한 단점들 때문에 Redux나 MobX를 더 선호합니다. 그리고 페이스북에서 새로운 상태관리 라이브러리
Recoli를 개발했습니다.
그럼 Recoli의 대한 장점도 소개해볼까요?? Recoli을 개발한 페이스북 리액트 코리아 그룹의 한 포스트에는 이러한 장점이 있다고 말해주었습니다.
- 공유상태(shared state)에도 React의 로컬 상태(local state)처럼 간단한 get/set 인터페이스로 사용할 수 있도록 boilerplate-free API를 제공합니다.(필요한 경우 reducers등으로 캡슐화 할 수 있다.
- 동시성 모드(Concurrent Mode)를 비롯한 다른 새로운 React의 기능들과의 호환 가능성도 갖습니다.
- 상태 정의는 증분 및 분산되므로 코드 분할이 가능합니다.
- 상태를 사용하는 컴포넌트를 수정하지 않고 상태를 파생된 데이터로 대체할 수 있습니다.
- 파생된 데이터를 사용하는 컴포넌트를 수정하지 않고도 파생된 데이터는 동기식과 비동기식간에 이동할 수가 있습니다.
- 역호환성 방식으로 전체 애플리케이션 상태를 유지하는 것은 쉬우므로, 유지된 상태는 애플리케이션 변경에도 살아남을 수 있습니다.
Recoli의 핵심 개념
Recoli을 직접 사용하면 atoms (공유 상태)에서 selectors(순수함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만 들 수 있습니다. Atoms는 컴포넌트가 구독할 수 있는 상태의 단위이며, Selectors는 atoms의 상태값을 동기 또는 비동기 방식을
통해서 변환해주는데, 하나씩 살펴보겠습니다.
(1) Atoms
atom은 상태의 단위로 ,값이 업데이트 되면 값을 구독(subscribe)한 컴포넌트는 다시 랜더링이 됩니다.
Atoms는 atom함수를 사용해 생성할 수 있습니다.
const fontSizeState = atom({
key: 'fontSizeState', // 여기서 key 값은 전역적으로 교유해햐함
default: 14, // fontSize에 크기 대한 기본값??
})
컴포넌트에서 atom을 읽고 쓰기위해선 useRecoilState 라는 훅을 사용해야 합니다. React의 useState와
비슷하지만 상태가 컴포넌트 간에 공유될수 있다는 차이가 있습니다.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
Click to Enlarge
</button>
);
}
(2) Selectors
Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수 함수(pure function)입니다. 상위의 atoms 또는 selectors가 업데이트 되면 하위의 selector함수도 다시 실행됩니다.
Selectors는 상태를 기반으로 하는 파생 데이터(매번 계산되는 대신 파생된데이터 저장)를 계산하는 데 사용됩니다.
최소한의 상태 집합만 atoms에 저장하고 다른 모든 파생되는 데이터는 selectors를 통해 계산함으로써 쓸모없는 상태의 보존을
방지하는 것이죠.(MobX의 computed와 유사).
Selectors는 selecto함수를 사용해서 정의합니다.
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
get 속성은 계산될 함수입니다. get인자를 통해 atoms와 다른 selectors에 접근할 수 있는데, 참조했던 다른 atoms나 selectos가 업데이트 되면서 이 함수도 다시 실행됩니다.
이 fontSizeLabelState 예시에서 selector는 fontSizeState라는 하나의 atom에 의존성의 가지고 있습니다. 개념적으로 fontSizeLabelState selector는 fontSizeState를 입력으로 사용되고 형식화된 글꼴 크기 레이블을 출력으로 반환해주는 순수 함수처럼 동작하는 것입니다.
Selectors는 useRecoilValue()를 사용해 읽을 수 가 있습니다.
useRecoilValue() 는 하나의 atom이나 selector를 인자로 받아 대응하는 값을 반환하는데요, fontSizeLabelState selector는 writable하지 않기 때문에 useRecoilState()를 이용하지 않습니다.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
const fontSizeLabel = useRecoilValue(fontSizeLabelState);
return (
<>
<div>Current font size: ${fontSizeLabel}</div>
<button onClick={setFontSize(fontSize + 1)} style={{fontSize}}>
Click to Enlarge
</button>
</>
)
}
Recoil 설치하고 사용해보기
그럼 Recoil을 직접 설치하고 사용해봅시다.
npm install recoil // 혹은 yarn add recoil
프로젝트 생성하시는 법은 지난시간에도 많이 알려드렸으니 빠르게 가봅시다. npm 혹은 yarn을 사용하셔서 recoil을 설치해주세요
src/App.js
import React from 'react'
import {
RecoilRoot,
atom,
useRecoilState,
} from "recoil"
function App() {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
);
}
export default App;
recoil은 이런식으로 코딩합니다. RecoilRoot 컴포넌트로 감싸주고 그안에서 상태관리를 해주는 것이죠, Redux의 Provider 같은 느낌이라 말할수 있습니다. 이제 atom이라는 함수를 사용해서 상태를 만들어 줄 건데요, atom은 하나의 상태를 관리하는 것이라고 보시면 됩니다. state가 변경되면 그것에 따라 컴포넌트가 리랜더링 됩니다.
src/App.js
import React from 'react'
import {
RecoilRoot,atom,useRecoilState,
} from "recoil";
const countState = atom({
key: "countState",
// 기본값 0
default: 0,
});
function App() {
return (
<RecoilRoot>
<Counter></Counter>
</RecoilRoot>
);
}
function Counter() {
const [count, setCount] = useRecoilState(countState);
const addCount = () => {
setCount(count + 1);
};
const subCount = () => {
setCount(count - 1);
}
return (
<div>
<button onClick={addCount}>더하기</button>
<button onClick={subCount}>빼기</button>
<p>{count}</p>
</div>
)
}
export default App;
atom으로 생성한 state는 useRecoilState를 사용하여 읽고 변경할 수 있습니다. 인자로 읽고 atom으로 생성한 변수를 넣어주면 됩니다. 그러면 useState를 사용할 때처럼 값과 setter를 받을 수 있습니다.
그리고 평소 카운터를 만들듯 함수를 작성해주시면 제대로 작동하는 카운터를 볼수있죠
이렇게 간편하게 더하기 빼기 기능을 구현할수 있습니다.
그럼 이번에는 다르게 구현해보겠습니다.
import "./App.css"
import { RecoilRoot } from "recoil"
import Counter from "./Counter";
function App() {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
)
}
export default App;
// atom.js
import { atom } from "recoil";
let countState = atom({
key: 'counter',
default:0,
});
export default countState;
import React from 'react'
import countState from './atoms'
import {
useRecoilState,
useRecoilValue,
useSetRecoilState,
useResetRecoilState
} from "recoil"
function Counter() {
const [counter, setCounter] = useRecoilState(countState);
// useState와 같지만, useRecoilState를 사용해 다른 파일에 있는 atom을 읽을 수 있다.
const currentCount = useRecoilValue(countState); // 읽기 전용
const counterHandler = useSetRecoilState(countState); // 값만 변경시키기
const resetCounter = useResetRecoilState(countState); // 디폴트 값 변경
const plusCount = () => {
counterHandler((pre) => pre + 1);
};
const minusCount = () => {
counterHandler((pre) => pre - 1);
}
return (
<div>
<div>
{/* <div>{counter}</div> */}
{/* 아래 코드도 작동됨 */}
{/* <button onClick={() => setCounter((num) => num + 1)}>+</button>
<button onClick={() => setCounter((num) => num - 1)}>-</button> */}
<div>{currentCount}</div>
<button onClick={plusCount}>+</button>
<button onClick={minusCount}>-</button>
<button onClick={resetCounter}>초기화</button>
</div>
</div>
)
}
export default Counter
증가 차감 초기화가 이상없이 되는걸 볼수 있습니다. 이번에는 셀렉터까지 추가 해볼께요
우선 atom.js 를 이렇게 바꿔줍니다.
import { atom } from "recoil";
let countState = atom({
key: 'counter',
default: 0,
});
let inputState = atom({
// 기존에서 추가된 아톰
key: 'input',
default: 0,
})
export {countState, inputState};
그리고 새로운 파일 selector.js에 아래의 코드를 추가해줍니다.
import { selector } from "recoil";
import { countState, inputState } from "./atoms";
const countStateSelector = selector({
key: 'CounterState',
get: ({ get }) => {
const inputVal = get(inputState);
const count = get(countState);
return `추가된 카운트는 ${inputVal}이며, 현재 카운트는 ${count}입니다.`;
},
});
export default countStateSelector;
마지막으로 Counter.js를 아래와 같이 변경해주세요.
import React from 'react'
import {countState, inputState} from './atoms';
import countStateSelector from './selector';
import {
useRecoilState,
useRecoilValue,
useSetRecoilState,
useResetRecoilState
} from "recoil";
function Counter() {
const [counter, setCounter] = useRecoilState(countState);
// useState와 같지만, useRecoilState를 사용해 다른 파일에 있는 atom을 읽을 수 있다.
const currentCount = useRecoilValue(countState); // 읽기 전용
const counterHandler = useSetRecoilState(countState); // 값만 변경시키기
const resetCounter = useResetRecoilState(countState); // 디폴트 값 변경
// 새로 추가한 코드
const currentInput = useRecoilValue(inputState);
const inputHandlerState = useSetRecoilState(inputState);
const resultValue = useRecoilValue(countStateSelector);
const plusCount = () => {
counterHandler((pre) => pre + 1);
};
const minusCount = () => {
counterHandler((pre) => pre - 1);
}
// 새로 추가된 코드
const inputHandler = (e) => {
let target = e.target.value;
inputHandlerState(target);
};
const submitCount = () => counterHandler((pre) => pre + Number(currentInput));
return (
<div>
<div>
{/* <div>{counter}</div> */}
<div>{currentCount}</div>
{/* 아래 코드도 작동됨 */}
{/* <button onClick={() => setCounter((num) => num + 1)}>+</button>
<button onClick={() => setCounter((num) => num - 1)}>-</button> */}
<button onClick={plusCount}>+</button>
<button onClick={minusCount}>-</button>
<button onClick={resetCounter}>초기화</button>
<div>
<input type="text" onChange={inputHandler}></input>
<button onClick={submitCount}>입력값 더하기</button>
<div>{resultValue}</div>
</div>
</div>
</div>
)
}
export default Counter
값을 + 버튼을 누르고 text에 입력값 4를 하고 버튼을 누를때 현재 카운트와 합산해서 더해주는 것을 볼수 있습니다.
end
이번시간에는 text를 입력하고 그값을 더해주는 것을 만들어 보았습니다. 더하기를 포함해서 방식만 아신다면 여러 값을 넣어서 더하기 를 포함한 빼기 곱하기도 넣어주실수 있으실겁니다.
아래 샘플코드를 보시고 천천히 익혀보세요
참고 사이트
'프론트 엔드 > React' 카테고리의 다른 글
오직 React 만을 위한 라이브러리 - recoil-TodoList 만들어보기 (0) | 2021.08.30 |
---|---|
[REACT 개발 필수]CRA(create-react-app)에 ESLint, Prettier 적용, 설정하는법 (0) | 2021.08.29 |
GraphQL 따라잡기 2탄 - query와 mutation (0) | 2021.08.29 |