1) atom 이란?
2) atom 값을 get & set
useRecoilState -> atom을 넣고 -> get -> atom 값 가져오기
useRecoilState -> atom을 넣고 -> set -> atom 값 바꾸기
selector -> get -> atom 값 가져오기 (+ atom 값 가져와서, 계산한 결과를 return 하기) // 원본훼손X
selector -> set -> atom 값 바꾸기 // 원본훼손O
useRecoilState -> selector를 넣고 -> get -> get의 return값 반환
(이때, selector get(atom)을 통해서, atom을 구독하여 값을 가져와서 계산한 뒤, 결과를 return값으로 반환할 수 있다) // 원본훼손X
useRecoilState -> selector를 넣고 -> set -> selector의 set을 실행시킨다 (함수 호출과 비슷)
(이때, set(atom, 새로운데이터)를 통해서, selector가 atom 값을 바꿀 수 있다) // 원본훼손O
3) store에서 비동기 처리는 selector 안에서만 가능하다.
4) 여러 컴포넌트에서 같은 atom을 구독할 수 있다.
5) atom()과 useRecoilState()라인은, 같은 파일에서 사용하지 못한다.
6) atom과 selector는 유니크한 key값으로 고유하게 구분된다. (키값이 존재하여, 상태를 컴포넌트간 공유)
1) atom, selector 불러오기 (1번)
2) atom, selector 사용하기 (2번)
// 1) useRecoilState(atom명 or selector명)
// useState()와 같이, 배열의 1번째 요소가 상태 값, 2번째 요소가 상태를 업데이트하는 함수 // setSave(새로운값)
const [ save, setSave ] = useRecoilState(saveCart);
// 2) useRecoilValue(atom명 or selector명)
// 상태 값만 필요한 경우에 사용
const save = useRecoilValue(saveCart);
// 3) useSetRecoilState(atom명 or selector명)
// 상태를 업데이트하는 함수만 필요한 경우에 사용 // setSave(새로운값)
const setSave = useSetRecoilState(saveCart);
// 4) useResetRecoilState(atom명)
// atom의 state를, default 값으로 reset 시키는 역할 // resetSave()
const resetSave = useResetRecoilState(setSave);
// <!-- store.js --> ---> store
import { atom, selector } from 'recoil';
export const Aatom = atom({
key: 'Aatom',
default: 0 // 기본값을 설정한다 (default는 = 기본값)
});
export const Bselector = selector({
key: 'Bselector',
// get: 이 안에서 get을 여러번 사용할 수 있다.
get: ({ get }) => { // 원본훼손X
return get(Aatom) * 10 // B 버튼 3번) Aatom을 구독해서 사용하고 있는 얘도 리렌더링되서, return Aatom의 new값 * 10
// C 버튼 4번) Aatom을 구독해서 사용하고 있던 얘까지 리렌더링되서, return Aatom의 new값 * 10
},
// set: { set, get } 모두 사용할 수 있다.
set: ({ set, get }, newValue) => { // 원본훼손O
// set(Aatom, newValue) // Aatom = newValue 이런식으로, 기존값 무시하고 재할당된다.
set(Aatom, get(Aatom) + newValue) // B 버튼 2번) Aatom = Aatom + newValue 이런식으로, count = count + 1 의 방식을 유지할 수 있다.
}
});
export const Cselector = selector({
key: 'Cselector',
get: ({ get }) => { // 원본훼손X
return get(Aatom) / 10 // B 버튼 4번) Aatom을 구독해서 사용하고 있던 얘까지 리렌더링되서, return Aatom의 new값 / 10
// C 버튼 3번) Aatom을 구독해서 사용하고 있는 얘도 리렌더링되서, return Aatom의 new값 / 10
},
set: ({ set, get }, newValue) => { // 원본훼손O
set(Aatom, get(Aatom) - newValue) // C 버튼 2번) Aatom = Aatom - newValue
}
});
// <!-- Apage.jsx --> ---> components
import { useState, useEffect } from 'react'
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil';
import { Aatom, Bselector, Cselector } from '../store/store'; // import { atom명, selector명 } from '../store/파일명'
function Apage() {
const [ aatom, setAatom ] = useRecoilState(Aatom)
useEffect(() => {
setAatom(1)
}, [])
const [ bselector, setBselector ] = useRecoilState(Bselector)
const BcallMe = () => { setBselector(7) }; // B 버튼 1번)
const [ cselector, setCselector ] = useRecoilState(Cselector)
const CcallMe = () => { setCselector(7) }; // C 버튼 1번)
useEffect(() => {
console.log('Aatom 값이 바뀌었어요!!');
}, [aatom]) // aatom으로 해야, 바뀌었을때 계속 적용된다. (만약, 이때 Aatom으로 하면 로드되었을때 딱 한번만 작동한다)
return (
<div>
{ aatom }
<br/>
{ bselector }
<br/>
{ cselector }
<br/>
<button onClick={ BcallMe }> B 버튼 </button>
<button onClick={ CcallMe }> C 버튼 </button>
</div>
)
}
export default Apage
// <!-- Bpage.jsx --> ---> components
import { useState, useEffect } from 'react'
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil';
import { Aatom } from '../store/store'; // import { atom명, selector명 } from '../store/파일명'
function Bpage() {
const [ A, setA ] = useRecoilState(Aatom) // Aatom값이 변할때마다 같이 변한다.
useEffect(() => {
setA(3)
}, [])
const wantKnow = () => {
setA(A + 10000) // 값이 계속 쌓인다. count = count + 1 의 방식을 유지할 수 있다.
}
return (
<div>
{ A }
<br/>
<button onClick={ wantKnow }> 확인합니다 </button>
</div>
)
}
export default Bpage
// <!-- store.js --> ---> store
import { atom, selector } from 'recoil';
import axios from 'axios';
export const Aatom = atom({
key: 'Aatom',
default: 0
});
export const Bselector = selector({
key: 'Bselector',
// get: 이 안에서 get을 여러번 사용할 수 있다. get은 atom과 selector 모두 가져올 수 있다.
get: ({ get }) => { // 원본훼손X
return get(Aatom) * 10 // B 버튼 3번) Aatom을 구독해서 사용하고 있는 얘도 리렌더링되서, return Aatom의 new값 * 10
// C 버튼 4번) Aatom을 구독해서 사용하고 있던 얘까지 리렌더링되서, return Aatom의 new값 * 10
},
// set: { set, get } 모두 사용할 수 있다.
set: ({ set, get }, newValue) => { // 원본훼손O
// set(Aatom, newValue) // Aatom = newValue 이런식으로, 기존값 무시하고 재할당된다.
set(Aatom, get(Aatom) + newValue) // B 버튼 2번) Aatom = Aatom + newValue 이런식으로, count = count + 1 의 방식을 유지할 수 있다.
}
});
export const Cselector = selector({
key: 'Cselector',
get: ({ get }) => { // 원본훼손X
return get(Aatom) / 10 // B 버튼 4번) Aatom을 구독해서 사용하고 있던 얘까지 리렌더링되서, return Aatom의 new값 / 10
// C 버튼 3번) Aatom을 구독해서 사용하고 있는 얘도 리렌더링되서, return Aatom의 new값 / 10
},
set: ({ set, get }, newValue) => { // 원본훼손O
set(Aatom, get(Aatom) - newValue) // C 버튼 2번) Aatom = Aatom - newValue
}
});
// 1번)
export const Dselector = selector({
key: 'Dselector',
get: async () => { // 비동기 처리는 get에서 한다.
const { data } = await axios.get('https://fakestoreapi.com/products');
const aa = 'aa'
const bb = 'bb'
console.log(data);
return { data, aa, bb }
} // 만약, 비동기처리한 데이터를 atom으로 갖고 싶으면, set에서 get으로 호출한 값을 atom에 넣어줘도 되고, use라인 사용해서 컴포넌트에서 atom에 넣어줘도 되고.
});
// <!-- Bpage.jsx --> ---> components
import { useState, useEffect } from 'react'
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil';
import { Aatom, Dselector } from '../store/store'; // import { atom명, selector명 } from '../store/파일명'
function Bpage() {
const [ A, setA ] = useRecoilState(Aatom) // Aatom값이 변할때마다 같이 변한다.
useEffect(() => {
setA(3)
}, [])
// 2번)
const { data, aa, bb } = useRecoilValue(Dselector)
const result = data.map((item) => (<div key={item.id}>{ item.id }</div>)) // data바로 사용 X, 풀어서만 사용가능. key값 꼭 설정해야한다.
return (
<div>
{ A }
<br/>
<br/>
{ result } // 3번) 결과가 1 ~ 20 까지
<br/>
{ aa }
<br/>
{ bb }
</div>
)
}
export default Bpage
// <!-- Apage.jsx --> ---> components
import { useState, useEffect } from 'react'
import { useRecoilValue, useSetRecoilState, useRecoilState, useResetRecoilState } from 'recoil';
import { Aatom, Bselector, Cselector, Dselector } from '../store/store'; // import { atom명, selector명 } from '../store/파일명'
function Apage() {
const [ aatom, setAatom ] = useRecoilState(Aatom)
useEffect(() => {
setAatom(1)
}, [])
const [ bselector, setBselector ] = useRecoilState(Bselector)
const BcallMe = () => { setBselector(7) }; // B 버튼 1번)
const [ cselector, setCselector ] = useRecoilState(Cselector)
const CcallMe = () => { setCselector(7) }; // C 버튼 1번)
useEffect(() => {
console.log('Aatom 값이 바뀌었어요!!');
}, [aatom]) // aatom으로 해야, 바뀌었을때 계속 적용된다. (만약, 이때 Aatom으로 하면 로드되었을때 딱 한번만 작동한다)
const { data, aa, bb } = useRecoilValue(Dselector) // 4번) 다른 컴포넌트에서 사용해도, 함수가 리렌더링되지 않고, 캐싱된 값을 가져온다.
const reset = useResetRecoilState(Aatom); // 5번)
// Aatom이 초기값으로 돌아가서, Aatom을 구독하고 있던 모든 컴포넌트들의 Aatom값 부분에, 초기값이 들어간다.
// 문제) Aatom의 초기값이 1이고, 현재값이 10일때 -> selector에서 Aatom을 구독하고 있었다 -> 이때, reset을 하면?
// 답) selector의 get부분이 return get(Aatom) * 10 으로 되어있었다면, return 10 * 10 에서 -> return 1 * 10 으로 바뀌어서 결과값이 반환된다.
return (
<div>
{ aa }
<br/>
{ aatom }
<br/>
{ bselector }
<br/>
{ cselector }
<br/>
<button onClick={ BcallMe }> B 버튼 </button>
<button onClick={ CcallMe }> C 버튼 </button>
<button onClick={ reset }> Reset 버튼 </button>
</div>
)
}
export default Apage
// <!-- main.jsx --> ---> Vite 버전
// <!-- index.js --> ---> Vue에서는 main.js
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { RecoilRoot } from 'recoil'; // store를 사용하기 위해서 필요한 단계 1
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// <React.StrictMode>
<RecoilRoot> // store를 사용하기 위해서 필요한 단계 2
<Suspense fallback={<div>Loading...</div>}> // store를 사용하기 위해서 필요한 단계 3 (비동기 처리 사용하기 위해서)
<App />
</Suspense>
</RecoilRoot>
// </React.StrictMode>
);
1) 매개변수 X
import { selector } from 'recoil';
import { productsApi } from '@apis/goodsApi';
export const productLists = selector({
key: 'productLists',
get: async () => { // 1번) 매개변수가 없을 때는
const data = await productsApi(); // 2번) 바로 api 불러오면 된다
const allLists: AllListsGuard = {};
const all = data.map((item: ProductGuard) => {
item.price = Math.round(item.price);
allLists[item.id] = item;
return item;
});
return { all, allLists };
}
});
2) 매개변수 O (selector + atom)
import { atom, selector } from 'recoil';
import { singleApi } from '@apis/goodsApi';
export const productId = atom({ // 1번) store를 사용하는 컴포넌트에서 set을 통해 해당 아톰을 변경하면
key: 'productId',
default: ''
});
export const singleProduct = selector({
key: 'singleProduct',
get: async ({ get }) => {
const data = await singleApi(get(productId)); // 2번) productId 아톰을 구독하고 있던 selector도 영향을 받는다
if (data === '') return { data };
data.price = Math.round(data.price);
return { data };
}
});
3) 매개변수 O (selectorFamily)
import { selectorFamily } from 'recoil';
import { singleApi } from '@apis/goodsApi';
export const singleProduct = selectorFamily({ // 1번) selector 대신 selectorFamily
key: 'singleProduct',
get: (productId: string | undefined) => async () => { // 2번) 앞에 매개변수 넣어주고
const data = await singleApi(productId); // 3번) 사용한다
if (data === '') return { data };
data.price = Math.round(data.price);
return { data };
}
});