グローバルなstateの管理をRecoilを使って超簡易版 @yukilulu0229

https://recoiljs.org/

https://ics.media/entry/210224/

## インストール

https://www.npmjs.com/package/recoil

```
npm i recoil
```

再レンダリングをある程度最適化してくれる

## 使い方

### 設定する

``` store/userState.jsx

import {atom} from "recoil"

export const userState = atom({
  key: "userState", // 名前を決めるファイル名を一緒にするのが望ましい
  default: {isAdmin: false}
})


```

### 範囲を選択する

```App.jsx
import React from 'react'
import './App.css'
import {RecoilRoot} from "recoil";
import AtomicDesign from "./components/AtomicDesign.jsx";
import CssSettings from "./components/CssSettings.jsx";
import ReactRouter from "./components/ReactRouter.jsx";
import Rendering from "./components/Rendering.jsx";
import {BrowserRouter, Link, Route, Routes} from "react-router-dom";
import {UserProvider} from "./providers/UserProvider.jsx";

function App() {
  return (
    <div className="App">
      <RecoilRoot>
        <BrowserRouter>
          <Routes>
            <Route path="/Rendering" element={<Rendering />} />
            <Route path="/CssSettings" element={<CssSettings />} />
            <Route path="/ReactRouter/*" element={<ReactRouter />} />
            <Route path="/AtomicDesign/*" element={<AtomicDesign />} />
          </Routes>
        </BrowserRouter>
      </RecoilRoot>
    </div>
  )
}

export default App

```

### useStateみたいのを定義

```Users.jsx
import React, {useContext, useState} from 'react';
import {useRecoilState} from "recoil";
import {userState} from "../../store/userState.js";

const Users = () => {
  const [userInfo, setUserInfo] = useRecoilState(userState)
  console.log(userInfo);
  const isAdmin = userInfo ? userInfo.isAdmin : false
  const onClickSwitch = () => {
    setUserInfo({isAdmin: !isAdmin})
  }
  return (
    <>
      <SContainer>
        <SecondaryButton onClick={onClickSwitch}>切り替え</SecondaryButton>
      </SContainer>
    </>
  );
};

export default Users;

```

- useRecoilStateがuseStateみたいなもの
- userStateの定義を呼び出す


```Top.jsx
import React, {useContext} from 'react';
import {useSetRecoilState} from "recoil";
import {userState} from "../../store/userState.js";
import {useNavigate} from "react-router-dom";

const Top = () => {
  const navigate = useNavigate();
  const setUserInfo = useSetRecoilState(userState)
  
  const onClickAdmin = () => {
    setUserInfo({isAdmin: true})
    navigate("/AtomicDesign/Users/")
  }
  const onClickGeneral = () => {
    setUserInfo({isAdmin: false})
    navigate("/AtomicDesign/Users/")
  }
  return (
    <DefaultLayout>
      <SContainer>
        <h2>Top Page</h2>
        <SecondaryButton onClick={onClickAdmin}>管理者ユーザー</SecondaryButton>
        <br/>
        <br/>
        <SecondaryButton onClick={onClickGeneral}>一般ユーザー</SecondaryButton>
      </SContainer>
    </DefaultLayout>
  );
};
export default Top;

```

- useSetRecoilStateでuseStateのsetXxx側だけを呼び出す
- 変更したいsetNameを呼び出してる


### 使いたい場所

```UserIconWithName
import React, {memo, useContext} from 'react';
import {useRecoilValue} from "recoil";
import {userState} from "../../../store/userState.js";

const UserIconWithName = memo((props) => {
  const {image, name} = props
  const userInfo = useRecoilValue(userState)
  
  const isAdmin = userInfo ? userInfo.isAdmin : false
  return (
    <SContainer>
      <SImage src={image} alt="プロフィール" />
      <SName>{name}</SName>
      {isAdmin && <SEdit>編集</SEdit>}
    </SContainer>
  );
});

export default UserIconWithName;
```

- useRecoilValueで使いたいの呼び出す
- userStateを使うことを知らせる

## useContextの場合

### providerで定義する

- UserContextを作成する
- UserProviderを作成する
- propsでchildrenを受け取ってUserContext.Providerで挟める形にする
- state管理したいものを定義する 今回はUserInfo その際にusaStateを変更ができる形にする
- valueでstateしたいものを渡す

```providers/UserProvider.tsx

import React, {createContext, useState} from 'react';

export const UserContext = createContext({});

export const UserProvider = (props) => {
  const {children} = props
  const [userInfo, setUserInfo] = useState(null) // 初期値null
  return (
    <UserContext.Provider value={{userInfo, setUserInfo}}>
      {children}
    </UserContext.Provider>
  )
}

```

### App.jsxに設定

```App.jsx
import React from 'react'
import './App.css'
import AtomicDesign from "./components/AtomicDesign.jsx";
import CssSettings from "./components/CssSettings.jsx";
import ReactRouter from "./components/ReactRouter.jsx";
import Rendering from "./components/Rendering.jsx";
import {BrowserRouter, Link, Route, Routes} from "react-router-dom";
import {UserProvider} from "./providers/UserProvider.jsx";

function App() {
  return (
    <div className="App">
      <UserProvider>
        <BrowserRouter>
          <Routes>
            <Route path="/Rendering" element={<Rendering />} />
            <Route path="/CssSettings" element={<CssSettings />} />
            <Route path="/ReactRouter/*" element={<ReactRouter />} />
            <Route path="/AtomicDesign/*" element={<AtomicDesign />} />
          </Routes>
        </BrowserRouter>
      </UserProvider>
    </div>
  )
}

export default App

```

UserProviderが使用できる範囲指定する

### 使いたい場所

```Top.jsx
import React, {useContext} from 'react';
import {UserContext} from "../../providers/UserProvider.jsx";
import SecondaryButton from "../atoms/button/SecondaryButton.jsx";
import DefaultLayout from "../templates/DefaultLayout.jsx";
import {useNavigate} from "react-router-dom";

const Top = () => {
  const navigate = useNavigate();
  const {setUserInfo} = useContext(UserContext)
  const onClickAdmin = () => {
    setUserInfo({isAdmin: true})
    navigate("/AtomicDesign/Users/")
  }
  const onClickGeneral = () => {
    setUserInfo({isAdmin: false})
    navigate("/AtomicDesign/Users/")
  }
  return (
    <DefaultLayout>
        <SecondaryButton onClick={onClickAdmin}>管理者ユーザー</SecondaryButton>
        <SecondaryButton onClick={onClickGeneral}>一般ユーザー</SecondaryButton>
    </DefaultLayout>
  );
};

export default Top;


```

- useContextを使ってグローバルに読み込めるようにする
- 今回使いたいUserContext作成させる
- useStateのsetUserInfoで変更できるようにする

### memoを使って再レンダリングの最適化をする

```UserIconWithName.tsx

import React, {memo, useContext} from 'react';
import styled from "styled-components";
import {UserContext} from "../../../providers/UserProvider.jsx";

const UserIconWithName = memo((props) => {
  const {image, name} = props
  const {userInfo} = useContext(UserContext)
  const isAdmin = userInfo ? userInfo.isAdmin : false
  return (
    <SContainer>
      <SImage src={image} alt="プロフィール" />
      <SName>{name}</SName>
      {isAdmin && <SEdit>編集</SEdit>}
    </SContainer>
  );
});

export default UserIconWithName;

```

- memoを使って再レンダリングが必要がないことを指定している
- これによって最適化する