본 게시물은 Recoil Hooks에 대한 내용을 담고 있습니다.
const [count, setCount] = useRecoilState(countState);
const increaseCount = () => {
setCount(count + 1);
}
EX)
// 1. state에 정의된 QuizDifficulty.ts를 불러오는 방법
import { QuizDifficultyState } from 'src/state';
...
const QuizDifficulty = () => {
// 2. recoilState의 사용방법 : Hook처럼 사용방법
const [quizDifficulty, setQuizDifficulty] = useRecoilState(
QuizDifficultyState,
)
const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
setQuizDifficulty(e.target.value);
};
return (
<select
data-testid={DIFFICULTY_SELECT_TEST_ID}
margin="16px 0px"
value={quizDifficulty}
onChange={handleChange}
>
</select>
);
};```
### useRecoilValue
- useRecoilState의 기능 중 get 만을 위한 hook
- recoil state의 값을 반환
```javascript
const count = useRecoilValue(countState);
const count = useRecoilValue(countState);
EX)
const resetCount = useResetRecoilState(countState);
...
<button onClick={resetCount}>reset count</button>
* atom 등록 시의 default값으로 atom을 되돌린다.
import { DefaultValue, selector } from "recoil";
import countState from "../atom/countState";
export default selector({
key: "countSelector",
get: ({get}): number => {
const count = get(countState);
return count + 1;
},
set: ({set, get}, newCount:any)=>{
return set(
countState,
newCount instanceof DefaultValue ? newCount : newCount+10,
)
}
})
변하지 않는 정적인 데이터를 보관하려고 상태 관리 라이브러리를 사용하지는 않을 것이다. 상태는 끊임없이 변한다. 스냅샷은 계속 변하는 상태의 "한 순간"이다. 상태가 동영상이라면 스냅샷은 동영상의 한 프레임인 것이다.
스냅샷은 세 가지 방식을 통해 가져올 수 있다. 모두 역할이 조금씩 다르기는 하지만useRecoilSnapshot(), useRecoilTransactionObserver(), useRecoilCallback()이라는 훅을 사용해 스냅샷을 구할 수 있다.
import {useRecoilSnapshot} from "recoil ";
function SnapshotCount() {
const snapshotList = useRef([]);
const snapshot = useRecoilSnapshot();
useEffect(()=>{
snapshotList.current = [...snapshotList.current, snapshot];
},[])
return (
<p>Snapshot count: {snapshotList.current.lenth}</p>
)
카운터의 값이 증가하면 상태가 변하고, 따라서 보관된 스냅샷의 갯수도 증가할 것이다. 카운터의 값이 증가하면 상태가 변하고, 따라서 보관된 스냅샷의 갯수도 증가할 것이다. 스냅샷은 상태가 변할 때마다 생성된다. 따라서 SnapshotCount 컴포넌트는 상태가 변할 때마다 렌더링 된다.
이 훅에 전달된 콜백 함수는 아톰 상태가 변경될 때마다 호출되지만 여러 업데이트가 동시에 일어나는 경우에는 묶어서 한 번만 호출될 수도 있다.
첫 번째 인수로 호출할 콜백 함수를 전달한다. 콜백 함수에 전달되는 첫 번째 인수는 snapshot과 previousSnapshot이라는 프로퍼티를 가진 객체인데 각각 현재 스냅샷과 이전 스냅샷을 의미한다. 공식 문서에 나타난 정의를 살펴보면 다음과 같다.
function useRecoilTransactionObserver_UNSTABLE(({
snapshot: Snapshot,
previousSnapshot: Snapshot,
}) => void)
보관 예제
import {useRecoilTransactionObserver_UNSTABLE} from 'recoil';
function SnapshotCount() {
const snapshotList = useRef([]);
useRecoilTransactionObserver_UNSTABLE(({snapshot}) => {
snapshotList.current = [...snapshotList.current, snapshot];
console.log("Snapshot updated", snapshotList);
});
return (
<p>Snapshot count: {snapshotList.current.length}</p>
);
}
useRecoilSnapshot()과 아주 큰 차이가 있다. useRecoilTransactionObserver는 컴포넌트를 다시 렌더링하지 않는다. 따라서 출력 결과물인 "Snapshot count" 부분의 숫자가 변경되지 않는다. 콘솔을 살펴보면 분명 콜백 함수는 상태가 변경될 때마다 실행되고 있지만 컴포넌트 렌더링에는 영향을 주지 않는 것이다. 스냅샷은 구할 수 있으면서도 컴포넌트를 다시 렌더링하지 않으므로 성능 면에서 분명한 이득이 있다.
useCallback과 같이 의존성에 따라 갱신되는 메모이즈된 함수를 생성한다. 다만, 생성된 함수에 스냅샷과 상태를 다루는 객체 및 함수가 함께 전달된다는 점이 다르다.
useCallback처럼 첫 번째 인수에는 메모이즈할 함수가 전달되고 두 번째 인수에는 의존성이 전달된다. 주의할 점은 useRecoilCallback에서 첫 번째 인수는 스냅샷 관련 기능을 전달하기 위해 한 번 더 감싸진 "실행할 함수를 만드는 함수"라는 것이다.
const log1 = useCallback(() => {
console.log('called with ', 'nothing');
});
const log2 = useRecoilCallback(({snapshot}) => () => {
console.log('called with ', snapshot);
});
useRecoilCallback에서 생성된 함수의 첫 번째 인수로 전달되는 객체에는 snapshot 객체 외에도 gotoSnapshot 함수, set 함수, reset 함수가 포함되어 있다.
import {useRecoilCallback} from 'recoil';
function SnapshotCount() {
const [snapshotList, setSnapshotList] = useState([]);
const updateSnapshot = useRecoilCallback(({ snapshot }) => () => {
setSnapshotList(prevList => [...prevList, snapshot]);
});
return (
<div>
<p>Snapshot count: {snapshotList.length}</p>
<button onClick={updateSnapshot}>현재 스냅샷 보관</button>
</div>
);
}
스냅샷 객체에는 getPromise와 getLoadable이라는 메소드가 있는데 이를 통해 상태값을 가져올 수 있다.
function SnapshotCount() {
const [snapshotList, setSnapshotList] = useState([]);
const updateSnapshot = useRecoilCallback(({ snapshot }) => async () => {
const count = await snapshot.getPromise(counter);
console.log("Count: ", count);
setSnapshotList(prevList => [...prevList, snapshot]);
});
return (
<div>
<p>Snapshot count: {snapshotList.length}</p>
<button onClick={updateSnapshot}>현재 스냅샷 보관</button>
</div>
);
}