
React에는 다음과 같은 한계가 존재
Recoli은 직교하지만 본질적인 방향 그래프를 정의하고 React 트리에 붙인다. 상태 변화는 이 그래프의 뿌리(aotms라고 부르는)로부터 순수함수를 거쳐 컴포넌트로 흐르는 다음과 같은 접근 방식을 따른다.
	npm install recoil우선 cra에 있는 App.js에서 RecoliRoot를 감싸주어야함
import { RecoilRoot } from 'recoil';
function App() {
  return (
    <div className="App">
      <RecoilRoot>
        
      </RecoilRoot>
    </div>
  );
}
export default App;
store.js
//atom 선언
import { atom } from 'recoil';
export const fontSizeState = atom({
  key: "fontSzieState",
  default: 14,
});
FontButton.jsx
import React from 'react'
import { useRecoilState } from 'recoil'
import { fontSizeState } from './store'
export default function FontButton() {
    const [fontSize,setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size)=> size+1)} style={{fontSize}}>
        
        Click to Enlarge
    </button>
  )
}
Text.jsx
import React from 'react'
import { useRecoilState } from 'recoil'
import { fontSizeState } from './store'
export default function Text() {
    const [fontSize,setFontSize] = useRecoilState(fontSizeState);
  return (
    <p style={{fontSize}}>This text will increase in size too.</p>
  )
}
import { RecoilRoot } from "recoil";
import FontButton from "./components/RecoilExeample/FontButton";
import Text from "./components/RecoilExeample/Text";
function App() {
  return (
    <div className="App">
      <RecoilRoot>
        <FontButton />
        <Text />
      </RecoilRoot>
    </div>
  );
}
export default App;
 클릭 시 버튼 사이즈와 text 사이즈가 커지게 된다.
RecoilRoot로 인해 provider 처럼 최상위 루트에서 자식들끼리  다른 component임에도 불구하고 똑같이 상태 변하는 것을 알 수 있다.

store.js
import { atom, selector } from "recoil";
export const fontSizeState = atom({
  key: "fontSzieState",
  default: 14,
});
export const fontSizeLabelState = selector({
  key: "fontSizeLabelState",
  get: ({ get }) => {
    const fontSize = get(fontSizeState);
    const unit = "px";
    return `${fontSize}${unit}`;
  },
});
FontButton.jsx
import React from 'react'
import { useRecoilState } from 'recoil'
import { fontSizeLabelState, fontSizeState } from './store'
export default function FontButton() {
    const [fontSize,setFontSize] = useRecoilState(fontSizeState);
    const [fontSizeLabel,setfontSizeLabel] = useRecoilState(fontSizeLabelState);
  return (
    <>
    <div> Current font size:{fontSizeLabel}</div>
    <button onClick={() => setFontSize((size)=> size+1)} style={{fontSize}}>        
        Click to Enlarge
    </button>
    </>
  )
}
selector는 atom이나 다른 값들이 바뀌면 자기도 같이 바뀌는데 무엇인가 더하거나 그 값을 변경을 취해서 보여준다.

CounterStore.js
import { atom, selector } from "recoil";
export const textState = atom({
  key: "textState", //이 아이디는 유니크해야함
  default: "",
});
export const charCountState = selector({
  key: "charCountState",
  get: ({ get }) => {
    const text = get(textState);
    return text.length;
  },
});
CharacterCounter.jsx
import React from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { charCountState, textState } from './CounterStore'
export default function CharacterCounter() {
  return (
    <div>
        <TextInput/>
        <CharacterCount/>
    </div>
  )
}
function TextInput(){
  const [text,setText] = useRecoilState(textState);
  const onChange = (event) =>{
    setText(event.target.value);
  }
  return(
    <div>
        <input type="text" value={text} onChange={onChange}/>
        <br/>
        Echo:{text}
    </div>
  )
}
function CharacterCount(){
  const count = useRecoilValue(charCountState);
  return <>Character Count:{count}</>
}useRecoliState는 get/set를 같이 가지고 있지만 useRecoilValue는 값만가져와서 사용한다.

msw를 이용하여 비동기 api 연결하기
handlers.js
import { rest } from "msw";
export const handlers = [
  rest.put("http://localhost:3000/api/user-name", async (req, res, ctx) => {
    const id = req.url.searchParams.get("id");
    return res(
      ctx.json({
        name: id === "1" ? "The one" : "The others",
      })
    );
  }),
];
CureentUserInfo.jsx
import axios from 'axios';
import React from 'react'
import { atom, selector, useRecoilValue } from 'recoil'
import ErrorBoundary from './ErrorBoundary';
const currentUserIdState = atom({
    key:'CurrentUserID',
    default:1,
})
const tableOfUser=[{name:'park'},{name:'min'}];
// const currentUserNameState = selector({
//     key:'CurrentUserName',
//     get:({get})=>{
//         return tableOfUser[get(cureentUserIdState)].name;
//     },
// })
const currentUserNameQuery = selector({
    key:'CurrentUserName',
    get: async({get})=>{
        const response = await axios.get(
            `/api/user-name?id=${get(currentUserIdState)}`
        )        
        return response.data.name;
    }
})
function CurrentUser() { 
    const userName = useRecoilValue(currentUserNameQuery);    
  return (
    <div>
        {userName}
    </div>
  )
}
export default function CureentUserInfo(){
    return (
        <React.Suspense fallback={<div>Loading...</div>}>
            <CurrentUser/>
        </React.Suspense>
    )
}React.Suspense는 currentUserNameQuery의 값으로 내려오게 되면 fallback으로 promise 요청중인 상태라면 그 외의 fallback의 상태를 보여준다. username이라는 값이 결정되기 전에 fallback으로 대신하여 상태를 전달하여 보여줌
errorboundary와 유사하다.

Errorboundary로 error 처리
ErrorBoundary.jsx
import React from 'react';
export default class ErrorBoundary extends React.Component {
    constructor(props) {
      super(props);
      this.state = { hasError: false };
    }
  
    static getDerivedStateFromError(error) {
      // 다음 렌더링에서 폴백 UI가 보이도록 상태를 업데이트 합니다.
      return { hasError: true };
    }
  
    componentDidCatch(error, errorInfo) {
      // 에러 리포팅 서비스에 에러를 기록할 수도 있습니다.
//      logErrorToMyService(error, errorInfo);
    }
  
    render() {
      if (this.state.hasError) {
        // 폴백 UI를 커스텀하여 렌더링할 수 있습니다.
        return <h1>Something went wrong.</h1>;
      }
  
      return this.props.children;
    }
  }CureentUserInfo.jsx
import axios from 'axios';
import React from 'react'
import { atom, selector, useRecoilValue } from 'recoil'
import ErrorBoundary from './ErrorBoundary';
const currentUserIdState = atom({
    key:'CurrentUserID',
    default:2,
})
const tableOfUser=[{name:'park'},{name:'min'}];
// const currentUserNameState = selector({
//     key:'CurrentUserName',
//     get:({get})=>{
//         return tableOfUser[get(cureentUserIdState)].name;
//     },
// })
const currentUserNameQuery = selector({
    key:'CurrentUserName',
    get: async({get})=>{
        const response = await axios.get(
            `/api/user-name?id=${get(currentUserIdState)}`
        )        
        return response.data.name;
    }
})
function CurrentUser() { 
    const userName = useRecoilValue(currentUserNameQuery);    
  return (
    <div>
        {userName}
    </div>
  )
}
export default function CureentUserInfo(){
    return (
    <ErrorBoundary>
        <React.Suspense fallback={<div>Loading...</div>}>
            <CurrentUser/>
        </React.Suspense>
    </ErrorBoundary>
    )
}error시에는 아래 문구가 나온다.
