[React Native] Recoil ep.2 API

๋ชจ๋ฆฌ์Šคยท2022๋…„ 5์›” 17์ผ
1

React-Native

๋ชฉ๋ก ๋ณด๊ธฐ
5/12
post-thumbnail

๐Ÿ“Œ Recoil ์‹œ์ž‘

React recoil์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ช‡๊ฐ€์ง€ API์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋ คํ•œ๋‹ค.


๐Ÿ“’ RecoilRoot

์šฐ์„  reoil ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ๋ถ€๋ชจ ํŠธ๋ฆฌ์— RecoilRoot ์„ ์–ธ์ด ํ•„์š”ํ•˜๋‹ค. React Native์—์„œ๋Š” ๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ RecoilRoot๋ฅผ ๋„ฃ๊ธฐ์— ๊ฐ€์žฅ ์ ํ•ฉํ•˜์—ฌ, App.js์— ์ ์šฉํ•œ๋‹ค.

import React from 'react';
import {RecoilRoot} from 'recoil';

function App() {
  return (
    <RecoilRoot>
    	<Screen />
    </RecoilRoot>
  );
}

๐Ÿ“’ atom()

atom์€ Recoil์˜ ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•œ๋‹ค. atom() ํ•จ์ˆ˜๋Š” ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ RecoilState ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

function atom<T>({
  key: string,
  default: T | Promise<T> | RecoilValue<T>,
  
  effects_UNSTABLE?: $ReadOnlyArray<AtomEffect<T>>,
  
  dangerouslyAllowMutability?: boolean,
}): RecoilState<T>
  • key: ๋‚ด๋ถ€์ ์œผ๋กœ atom์„ ์‹๋ณ„ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด. ์ด ๋ฌธ์ž์—ด์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ๋‹ค๋ฅธ atom๊ณผ selector์— ๋Œ€ํ•ด ๊ณ ์œ ํ•ด์•ผ ํ•œ๋‹ค.
  • default: atom์˜ ์ดˆ๊นƒ๊ฐ’ ๋˜๋Š” Promise ๋˜๋Š” ๋™์ผํ•œ ํƒ€์ž…์˜ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋‹ค๋ฅธ atom์ด๋‚˜ selector.
  • effects_UNSTABLE: atom์„ ์œ„ํ•œ ์„ ํƒ์ ์ธ Atom Effects ๋ฐฐ์—ด.
  • dangerouslyAllowMutability: Recoil์€ atom์„ ์ด์šฉํ•ด ๋‹ค์‹œ ๋ Œ๋”๋ง ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์— ์–ธ์ œ ์•Œ๋ ค์•ผ ํ• ์ง€ ์•Œ๊ธฐ ์œ„ํ•ด atom์˜ ์ƒํƒœ ๋ณ€ํ™”์— ์˜์กดํ•œ๋‹ค. ๋งŒ์•ฝ atom์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ, ์ด๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๋“ฑ๋ก๋œ ์ปดํฌ๋„ŒํŠธ์— ์ œ๋Œ€๋กœ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ €์žฅ๋œ ๋ชจ๋“  ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์˜ต์…˜์„ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก atom๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” Hooks

  • useRecoilState(): atom์„ ์ฝ๊ณ  ์“ฐ๋ ค๊ณ  ํ•  ๋•Œ ์ด Hook์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด Hook์€ atom์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋“ฑ๋กํ•˜๋„๋ก ํ•œ๋‹ค.
  • useRecoilValue(): atom์„ ์ฝ๊ธฐ๋งŒ ํ•  ๋•Œ ์ด Hook์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด Hook์€ atom์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋“ฑ๋กํ•˜๋„๋ก ํ•œ๋‹ค.
  • useSetRecoilState(): atom์— ์“ฐ๋ ค๊ณ ๋งŒ ํ•  ๋•Œ ์ด Hook๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • useResetRecoilState(): atom์„ ์ดˆ๊นƒ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์ด Hook์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • useRecoilCallback(): ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋“ฑ๋ก๋˜์ง€ ์•Š๊ณ  atom์˜ ๊ฐ’์„ ์ฝ์–ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•œ๋‹ค.
    ** Hook API์— ๋Œ€ํ•ด์„  ๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ๋กœ ํ•˜์ž. **
import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
import {atom, useRecoilState} from ' recoil';

const counter = atom({
  ket: 'myCounter',
  default: 0,
});

function Counter() {
  const [count, setCount] = useRecoilState(counter);
  const incrementByOne = () => setCount(count + 1);
  
  return (
    <View>
    	<Text>Count: ${count}</Text>
		<TouchableOpacity onPress={()=> incrementByOne}>
    		<Text>Increment<Text>
    	</TouchableOpacity>
    </View>
  );
}

๐Ÿ“’ selector()

selector ๋Š” Recoil์—์„œ ํ•จ์ˆ˜๋‚˜ ํŒŒ์ƒ๋œ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ์ฃผ์–ด์ง„ ์ข…์†์„ฑ ๊ฐ’ ์ง‘ํ•ฉ์— ๋Œ€ํ•ด ํ•ญ์ƒ ๋™์ผํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ถ€์ž‘์šฉ์ด ์—†๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
get ํ•จ์ˆ˜๋งŒ ์ œ๊ณต๋˜๋ฉด selector๋Š” ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ RecoilValueReadOnly ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. set ํ•จ์ˆ˜๊ฐ€ ์ œ๊ณต๋˜๋ฉด selector๋Š” ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ RecoilState ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

function selector<T>({
  key: string,
  
  get: ({
    get: GetRecoilValue
  }) => T | Promise<T> | RecoilValue<T>,
  
  set?: (
  	{
  		get: GetRecoilValue,
  		set: SetRecoilState,
  		reset: ResetRecoilState,
	},
    newValue: T | DefaultValue,
  ) => void,
  
  dangerouslyAllowMutability?: boolean,
})
type ValueOfUpdater<T> = T | DefaultValue | ((prevValue: T) => T | DefaultValue);
type GetRecoilValue = <T>(RecoilValue<T>) => T;
type SetRecoilState = <T>(RecoilState<T>, ValueOrUpdater<T>) => void;
type ResetRecoilState = <T>(RecoilState<T>) => void;
  • key: ๋‚ด๋ถ€์ ์œผ atom์„ ์‹๋ณ„ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด. ์ด ๋ฌธ์ž์—ด์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ๋‹ค๋ฅธ atom๊ณผ selector์— ๋Œ€ํ•ด ๊ณ ์œ ํ•ด์•ผ ํ•œ๋‹ค. ์ง€์†์„ฑ์„ ์œ„ํ•˜์—ฌ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด ์‹คํ–‰ ์ „๋ฐ˜์— ๊ฑธ์ณ ์•ˆ์ •์ ์ผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.
  • get: ํŒŒ์ƒ๋œ ์ƒํƒœ์˜ ๊ฐ’์„ ํ‰๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜. ๊ฐ’์„ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋น„๋™๊ธฐ์ ์ธ Promise๋‚˜ ๋˜๋Š” ๊ฐ™์€ ์œ ํ˜•์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋‹ค๋ฅธ atom์ด๋‚˜ selector๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋‹ค์Œ ์†์„ฑ์„ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
๋‹ค๋ฅธ atom์ด๋‚˜ selector๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ์ฐพ๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜. ์ด ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ ๋ชจ๋“  atom๊ณผ selector๋Š” ์•”์‹œ์ ์œผ๋กœ selector์— ๋Œ€ํ•œ __์˜์กด์„ฑ__ ๋ชฉ๋ก์— ์ถ”๊ฐ€๋œ๋‹ค.. Selector์˜ ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋˜๋ฉด Selector๊ฐ€ ๋‹ค์‹œ ํ‰๊ฐ€๋œ๋‹ค.
  • set?: ์ด ์†์„ฑ์ด ์„ค์ •๋˜๋ฉด selector๋Š” ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ฝœ๋ฐฑ ๊ฐ์ฒด์™€ ์ƒˆ๋กœ ์ž…๋ ฅ ๊ฐ’์ด ์ „๋‹ฌ๋œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ selector๋ฅผ ์žฌ์„ค์ •ํ•  ๊ฒฝ์šฐ ์ƒˆ๋กœ ์ž…๋ ฅ ๊ฐ’์€ T ํƒ€์ž…์˜ ๊ฐ’ ๋˜๋Š” DefaultValue ํƒ€์ž…์˜ ๊ฐ์ฒด์ผ ์ˆ˜ ์žˆ๋‹ค. ์ฝœ๋ฐฑ์—๋Š” ๋‹ค์Œ์ด ํฌํ•จ๋œ๋‹ค.
    get - ๋‹ค๋ฅธ atom์ด๋‚˜ selector๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ์ฐพ๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜. ์ด ํ•จ์ˆ˜๋Š” selector๋ฅผ ์ฃผ์–ด์ง„ atom์ด๋‚˜ selector๋ฅผ ๊ตฌ๋…ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    set - ์—…์ŠคํŠธ๋ฆผ Recoil ์ƒํƒœ์˜ ๊ฐ’์„ ์„ค์ •ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜. ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” Recoil ์ƒํƒœ, ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ƒˆ๋กœ์šด ๊ฐ’์ด๋‹ค. ์ƒˆ๋กœ์šด ๊ฐ’์€ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๋‚˜ ์žฌ์„ค์ • ์•ก์…˜์„ ์ „ํŒŒํ•˜๋Š” DefaultValue ๊ฐ์ฒด์ผ ์ˆ˜ ์žˆ๋‹ค.

  • dangerouslyAllowMutability: Selector๋Š” ํŒŒ์ƒ๋œ ์ƒํƒœ์˜ ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๋ฅผ ํƒ€๋‚˜๋‚ด๋ฉฐ ํ•ญ์ƒ ๋™์ผํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•ด selector์— ์ €์žฅ๋œ ๋ชจ๋“  ๊ฐ’์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ณ ์ •๋˜์–ด ์žˆ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์žฌ์ •์˜ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋™์  ์˜์กด์„ฑ

์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ selector๋Š” ์˜์กด์„ฑ์„ ๊ธฐ์ค€์œผ๋กœ selector์˜ ๊ฐ’์„ ํ‰๊ฐ€ํ•˜๋Š” get ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๋Š”๋‹ค. ์˜์กด์„ฑ ์ค‘ ์–ด๋– ํ•œ ๊ฒƒ์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด selector๋Š” ๋‹ค์‹œ ํ‰๊ฐ€๋œ๋‹ค. Selector๋ฅผ ํ‰๊ฐ€ํ•  ๋•Œ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š” atom์ด๋‚˜ selector๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์˜์กด์„ฑ์ด ๋™์ ์œผ๋กœ ๊ฒฐ์ •๋œ๋‹ค. ์ด์ „ ์˜์กด์„ฑ์˜ ๊ฐ’์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ถ”๊ฐ€์ ์ธ ์˜์กด์„ฑ์„ ๋™์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Recoil์€ Selector๊ฐ€ ํ˜„์žฌ ์—…๋ฐ์ดํŠธ๋˜์–ด์ง„ ์˜์กด์„ฑ ์ง‘ํ•ฉ๋งŒ ๊ตฌ๋…ํ•˜๋„๋ก ํ˜„์žฌ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ๊ทธ๋ž˜ํ”„๋ฅผ ์ž๋™์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ mySelector๋Š” toggleState atom๋ฟ๋งŒ ์•„๋‹ˆ๋ผ toggleState์— ์˜์กดํ•˜๋Š” selectorA ๋˜๋Š” selectorB selector๋„ ์˜์กดํ•œ๋‹ค.

const toggleState = atom({ key: 'Toggle', default: false });

const mySelector = selector({
  key: 'MySelector',
  get: ({get}) => {
    const toggle = get(toggleState);
    if (toggle) {
      return get(selectorA);
    } else {
      return get(selectorB);
  }
});

์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ Selector

์–‘๋ฐฉํ–ฅ selector๋Š” ์ž…๋ ฅ ๊ฐ’์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ  ๋ฐ์ดํ„ฐ ํ๋ฆ„ ๊ทธ๋ž˜ํ”„๋ฅผ ๋”ฐ๋ผ ์—…์ŠคํŠธ๋ฆผ์—์„œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ „ํŒŒํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ selector๋ฅผ ์ƒˆ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ selector๋ฅผ ์žฌ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž…๋ ฅ ๊ฐ’์€ selector๊ฐ€ ๋‚˜ํƒ€๋‚ด๋Š” ํƒ€์ž…๊ณผ ๋™์ผํ•˜๊ฑฐ๋‚˜ ์žฌ์„ค์ • ์ž‘์—…์„ ๋‚˜ํƒ€๋‚ด๋Š” DefaultValue ๊ฐ์ฒด ์ค‘ ํ•˜๋‚˜์ด๋‹ค.

์ด ๊ฐ„๋‹จํ•œ selector๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ atom์„ ๊ฐ์‹ธ์„œ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ด๊ฒƒ์€ ๋‹จ์ง€ ์„ค์ •๊ณผ ์žฌ์„ค์ • ์ž‘์—…์„ ์—…์ŠคํŠธ๋ฆผ atom๊นŒ์ง€ ํ†ต๊ณผํ•œ๋‹ค.

const proxySelector = selector({
  key: 'ProxySelector',
  get: ({get}) => ({...get(myAtom), extraField: 'hi'}),
  set: ({set}, newValue) => set(myAtom, newValue),
});

์ด selector๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๋ฏ€๋กœ ์ž…๋ ฅ ๊ฐ’์ด DefaultValue์ธ์ง€ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.

const transformSelector = selector({
  key: 'TranformSelector',
  get: ({get}) => get(myAtom) * 100,
  set: ({set}, newValue) => 
  		set(myAtom, newValue instanceof DefaultValue ? newValue : newValue / 100),
});

๋น„๋™๊ธฐ Selector

Selector๋Š” ๋˜ํ•œ ๋น„๋™๊ธฐ ํ‰๊ฐ€ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ Promise๋ฅผ ์ถœ๋ ฅ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

const myQuery = selector({
  key: 'MyQuery',
  get: async ({get}) => {
    return await myAsyncQuery(get(queryParamState));
  },
});

๐Ÿ“’ ์ธ์šฉ ์ถœ์ฒ˜

https://recoiljs.org/ko/docs/api-reference/core/atom
https://recoiljs.org/ko/docs/api-reference/core/selector

profile
๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœ ๋…ธํŠธ :)

0๊ฐœ์˜ ๋Œ“๊ธ€