Recoil Basic

recoil ๊ด€๋ จ reference


https://kitemaker.co/blog/lessons-learned-from-moving-to-recoil


  • react-query๋Š” ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • redux, mobx ๋“ฑ์€ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

https://velog.io/@yrnana/react-query๊ฐ€-redux๊ฐ™์€-์ „์—ญ-์ƒํƒœ๊ด€๋ฆฌ-๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ-๋Œ€์ฒดํ• -์ˆ˜-์žˆ์„๊นŒ

recoil ์ฐธ๊ณ ๊ธ€ -https://medium.com/humanscape-tech/recoil-์•Œ์•„๋ณด๊ธฐ-285b29135d8e

https://abangpa1ace.tistory.com/212 - atomFamily(), selectorFamily()

https://youtu.be/JvWukLAdS_8 - recoil tutorial

https://recoiljs.org/ko/docs/guides/asynchronous-data-queries/ - recoil ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

https://blog.woolta.com/categories/1/posts/209

https://taegon.kim/archives/10125

https://www.youtube.com/watch?v=JvWukLAdS_8 - recoil ๋™์˜์ƒ ์„ค๋ช…

https://www.youtube.com/watch?v=-_IzPd_bFNk - recoil ๋™์˜์ƒ ์„ค๋ช…


selectorFamily - ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋„˜๊ธธ๋•Œ


https://velog.io/@juno7803/Recoil-Recoil-200-ํ™œ์šฉํ•˜๊ธฐ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

useRecoilState() ์—ญํ• ์„ ๋ฐ˜์œผ๋กœ ์ชผ๊ฐœ๋ฉด

  1. useRecoilValue() value๋งŒ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ

  2. useSetRecoilState() state๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ๋งŒ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ

import { useRecoilValue, useSetRecoilState } from 'recoil';
import { cookieState } from '../../state';

const cookies = useRecoilValue(cookieState);
const setCookies = useSetRecoilState(cookieState);
  1. useResetRecoilState() ์ธ์ž๋กœ ๋ฐ›์•„์˜จ atom์˜ state๋ฅผ default ๊ฐ’์œผ๋กœ reset ์‹œํ‚ค๋Š” ์—ญํ• 

const resetCookies = useResetRecoilState(cookieState);

selectorํ•จ์ˆ˜:

  1. selector๊ฐ€ atom์„ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ์ด๋ฏธ ์„ ์–ธ๋œ atom์ด ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ, ๋‹ค์‹œ ์‹คํ–‰๋จ(atom ๊ตฌ๋… ๊ธฐ๋Šฅ)

  2. ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๋Š” ๋น„๋™๊ธฐ ๊ฐ’(response.data)์„ ์ž์‹ ์˜ ๊ฐ’์œผ๋กœ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Œ

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,
})
  • selector๋Š” read-only ํ•œ RecoilValueReadOnly

๊ฐ์ฒด๋กœ์„œ return ๊ฐ’ ๋งŒ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ  ๊ฐ’์„ set ํ•  ์ˆœ ์—†๋Š” ํŠน์ง•

state.js

export const cookieState = atom({
  key: 'cookieState',
  default: []
});

export const getCookieSelector = selector({
  key: "cookie/get",
  get: async ({ get }) => {
    try{
      const { data } = await client.get('/cookies');    
      return data.data;
    } catch (err) {
    	throw err;
    }
  },
  set: ({set}, newValue)=> {
    set(cookieState, newValue)
  }
});
  • key: selector๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ id, ์ฆ‰ key ๊ฐ’์„ ์˜๋ฏธ

  • get: ์—๋Š” derived state ๋ฅผ return ํ•˜๋Š” ๊ณณ. ์˜ˆ์‹œ ์ฝ”๋“œ์—์„œ๋Š” api call์„ ํ†ตํ•ด ๋ฐ›์•„์˜จ data๋ฅผ return,

    ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” atom์˜ ๊ฐ’์ด setState์— ์˜ํ•ด ์ˆ˜์ •๋์„ ๋•Œ, get์— ํ• ๋‹น๋œ ํ•จ์ˆ˜๊ฐ€ ์žฌ์‹คํ–‰๋จ.

    const cookie = useRecoilValue(selector) ์ด๋ ‡๊ฒŒ ์กฐํšŒ ๊ฐ€๋Šฅ

  • set: writeable ํ•œ state ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ return ํ•˜๋Š” ๊ณณ

    selector๋Š” read-only ํ•œ return ๊ฐ’(RecoilValue)๋งŒ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— set์œผ๋กœ๋Š” writeableํ•œ atom ์˜ RecoilState ๋งŒ ์„ค์ • ๊ฐ€๋Šฅ

set: ({set}, newValue) => { set(getCookieSelector, newValue) } // incorrect : cannot allign itself
 
set: ({set}, newValue) => { set(cookieState, newValue) } // correct : can allign another upstream atom that is writeable RecoilState
const [cookie, setCookie] = useRecoilState(cookieState);

Suspense, ๋น„๋™๊ธฐ ์ƒํƒœ ์ฒ˜๋ฆฌ

import React,{ Suspense } from 'react';
import { Cookies } from '../components';

const App = () => {
  return(
    <RootRecoil>
      <Suspense fallback={<div>Loading...</div>}> // shimmer ์ฃผ์ž… ๊ฐ€๋Šฅ
        <Cookies />
      </Suspense>
    </RootRecoil>
   );
}

export default App;

Suspense๋Œ€์‹  Recoil์˜ Loadable์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค

import { getCookieSeletor } from '../../reocil';
import { useRecoilState, useRecoilValueLoadable } from 'recoil';

const Cookies = () => {
  const cookieLoadable = useRecoilValueLoadable(getCookieSelector);

  switch(cookieLoadable.state){
    case 'hasValue':
      return (
        <>
          (<div>
    	    {cookieLoadable.contents.map(cookie =>(
              <Card
                cookies={cookie}
                key={cookie.id}
                idx={cookie.id}
               />
            ))}
	     </div>)
	  </>
	});
     case 'loading':
  	return <Loading />;
     case 'hasError':
     	throw cookieLoadable.contents;
}

export default Cookies;

Loadable ๊ฐ์ฒด

const cookieLoadable = useRecoilValueLoadable(getcookieSelector);

state : hasValue, hasError, loading atom ์ด๋‚˜ selector์˜ ์ƒํƒœ๋ฅผ ๋งํ•˜๋ฉฐ, ์•ž์˜ ์„ธ ๊ฐ€์ง€ ์ƒํƒœ

contents: atom์ด๋‚˜ contents์˜ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ’ ๋ณด์œ .

  • hasValue์ƒํƒœ์ผ ๋• value

  • hasError์ผ ๋• Error๊ฐ์ฒด

  • loading ์ผ ๋• Promise

selector๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ’์„ ์บ์‹ฑ

๊ธฐ์กด์˜ atom์„ ์ด์šฉํ•œ ๋ฐฉ์‹์€ ๋งค๋ฒˆ api call์„ ํ•˜๊ณ  ์žˆ๋Š” ๋ฐ˜๋ฉด, selector๋กœ ๋ฐ”๊ฟ”์„œ ๊ตฌํ˜„ํ–ˆ์„ ๋•, ํ•œ๋ฒˆ call์„ ํ–ˆ๋˜ api์— ๋Œ€ํ•œ ์บ์‹ฑ์ด ์ด๋ฃจ์–ด์ ธ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Œ

์บ์‹œ(cache)๋Š” ์ปดํ“จํ„ฐ ๊ณผํ•™์—์„œ ๋ฐ์ดํ„ฐ๋‚˜ ๊ฐ’์„ ๋ฏธ๋ฆฌ ๋ณต์‚ฌํ•ด ๋†“๋Š” ์ž„์‹œ ์žฅ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์บ์‹œ๋Š” ์บ์‹œ์˜ ์ ‘๊ทผ ์‹œ๊ฐ„์— ๋น„ํ•ด ์›๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ ‘๊ทผํ•˜๋Š” ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ๋‚˜ ๊ฐ’์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๋Š” ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•œ๋‹ค. ์บ์‹œ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ณต์‚ฌํ•ด ๋†“์œผ๋ฉด ๊ณ„์‚ฐ์ด๋‚˜ ์ ‘๊ทผ ์‹œ๊ฐ„ ์—†์ด ๋” ๋น ๋ฅธ ์†๋„๋กœ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค


atomFamily

์ด Family ๋ฉ”์„œ๋“œ๋“ค์€ atom(ํ˜น์€ selector)์„ ๋ฆฌํ„ดํ•˜๋Š” ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜์ด๋‹ค. ์ธ์ž(params)๋ฅผ ๋ฐ›์•„, ์ด๋ฅผ ๋ฐ˜์˜ํ•œ ๋™์ ์ธ ์ƒํƒœ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ทธ๋ ‡๊ธฐ์—, Recoil ๋‚ด ๋‹ค๋ฅธ ์ƒํƒœ๊ฐ’์ด ์•„๋‹Œ, ์™ธ๋ถ€์ธ์ž(Query Params ๋“ฑ)๋ฅผ ํ™œ์šฉํ•œ ์ƒํƒœ๊ฐ’์„ ๊ด€๋ฆฌํ•˜๊ธฐ์— ์œ ๋ฆฌํ•˜๋‹ค.

์™ธ๋ถ€์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ’์„ ๋ฐ›์•„์™€์„œ selector์— ์ ์šฉํ•ด์•ผ ํ•  ๊ฒฝ์šฐ์— selectorFamily

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ˜์˜ํ•œ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ : selectorFamily()

selector์™€ selectorFamily ๋‘ ๋ฐฉ์‹์˜ ์ฐจ์ด๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์œ ๋ฌด

๊ตณ์ด ์ƒํƒœ์— ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฐ’์ด๋ผ๋ฉด selectorFamily๋ฅผ ์‚ฌ์šฉ

`https://baseUrl.com/type=${apiTypes}`
// state.jsx

export const githubRepo = selectorFamily({
  key: "github/get",
  get: (githubId) => async () => {
    if (!githubId) return "";

    const { data } = await axios.get(
      `https://api.github.com/repos/${githubId}`
    );
    return data;
  },
});
// Github.jsx

import { useRecoilValue } from 'recoil';
import { selectorFamily } from '../../state';

const Github = () => {
  const githubId = 'juno7803';
  const githubRepos = useRecoilValue(githubRepo(githubId));
  
  return(
    <>
      <div>Repos : {githubRepos}</div>
    </>
  )

}
export default Github;

Last updated