React Recoil


Recoilとは?
Recoilは、応答のためのステータス管理ライブラリです.
RecoilはAtomを使用して各コンポーネントのステータスを管理し、usStateと同じAPIを使用するため、作成と使用が非常に容易です.


後部座席画像の画像
これらは私個人が最も理解しやすい絵ですが、反応には以下のいくつかの制限があります.
  • コンポーネントのステータスは、共有するために共通の祖先によってプッシュされる必要があります.このコンポーネントには、再レンダリングが必要なツリーが含まれる場合があります.
  • コンテキストは、単一の値のみを格納し、一意の消費者を有する無限の値セットを格納することはできない.
  • の2つの方法は、ツリーの葉(使用状態の位置)をツリーの上部(状態の位置が必要)にコード分割することを困難にする.
    ソース
  • アプリケーション内のステータス変更が他の複数のコンポーネントに影響を及ぼす場合、このステータス変更は他のコンポーネントでも必要になり、上記の制限は不便になります.
    したがって,状態管理ライブラリを用いて複数の状態管理ライブラリがあるが,RecoilはAPIと意味体系および動作方式を可能な限り「reactish」に持ち込んでこの問題を解決するために作成された.
    コアコンセプト
    スタート
  • npm i recoilまず後部座席を設置します.
  • RecoilRoot
    recoilを使用するには、親ツリーの場所にRecoilRootを表示する必要があります.
  • 例)
    //index.ts
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import { RecoilRoot } from "recoil";
    
    ReactDOM.render(
      <React.StrictMode>
        <RecoilRoot> //***
          <App />
        </RecoilRoot>
      </React.StrictMode>,
      document.getElementById('root')
    );
    Atoms
  • 原子は状態単位である.
  • コンポーネントは購読可能なreact状態であると考えられる.
  • atomが更新されると、そのatomを購読したすべてのコンポーネントが再レンダリングされます.
  • の複数の構成部品で同じatomを購読すると、これらの構成部品は同じ状態を共有します.
  • atomを生成するには、一意のキー値とデフォルト値を設定する必要があります.
  • //atoms.ts
    import {atom} from "recoil";
    
    export const counterAtom = atom({
    	key: "counter",
    	default : 0
    })
    atom機能によって生成されます.(鍵は一意でなければなりません)
    デフォルト値はnumber、string、object、array、functionなどです.
    //ComponentIncrement.js
    import { counterAtom } from "./atoms";
    import { useRecoilState } from "recoil";
    
    export default function ComponentIncrement() {
    	const [counter, setCounter] = useRecoilState(counterAtom);
    	const onIncrementClick = () => setCounter(counter + 1);
    	return (
    		<div>
    			<p>Component</p>
    			<p>{counter}</p>
    			<input onClick={onIncrementClick} type="submit" value="Increment" />
    		</div>
    	)
    }
    userRecoilstateはuserStateと似ています.
    カウンタでカウンタの値を表示したり、setCounterでカウンタの値を変更したりできます.
    InputをクリックするたびにonIncrementClick関数が実行され、+1でステータス値が変更されます.
    //ComponentValue.js
    import { useRecoilValue } from "recoil";
    import { counterAtom } from "./atoms";
    
    export default function ComponentValue() {
    	const counter = useRecoilValue(counterAtom);
    	return (
    		<div>
    			<p>Component Value Only</p>
    			<p>{counter}</p>
    		</div>
    	)
    }
    useRecoilValueはステータス値を読み込みます.
    //App.js
    import ComponentIncrement from "./ComponentIncrement";
    import ComponentValue from "./ComponentValue";
    
    export default function App() {
      return (
        <div>
          <ComponentIncrement />
          <ComponentValue />
        </div>
      );
    }

    「コンポーネント増分」と「コンポーネント価値」は別々ですが、「コンポーネント増分」の入力ボタンに従って反Atom値が共有されていることがわかります.
  • UserRecoilValue():atomの内容を読み取ることができます.
  • ユーザーSetRecoilstate():atomの内容を変更できます.
  • ユーザー・リカバリ・ステータス():ユーザー・リカバリ値およびユーザー・リカバリ・ステータスとともに使用できます.
  • selectors
    セレクタは、ステータスベースの派生データを計算するために使用されます.これは、最小の状態セットがatomに格納され、他のすべてがこの最小状態の関数として効率的に計算されるため、繰り返し状態を回避することができる.
    この機能メソッドは、どのコンポーネントが必要なのか、どのステータスに依存するのかを追跡します.
  • キーは必須で、一意でなければなりません.
  • getは必須で、setはオプションです.
  • ToDo List例
    //atoms.ts
    import { atom, selector } from "recoil";
    
    export enum Categories {
    	"TO_DO" = "TO_DO",
    	"DOING" = "DOING",
    	"DONE" = "DONE",
    }
    
    export interface IToDo {
    	text: string;
    	id: number;
    	category: Categories;
    }
    
    export const categoryState = atom<Categories>({
    	key: "category",
    	default: Categories.TO_DO,
    });
    
    export const toDoState = atom<IToDo[]>({
    	key: "toDo",
    	default: [],
    });
    
    export const toDoSelector = selector({
    	key: "toDoSelector",
    	get: ({ get }) => {
    		const toDos = get(toDoState);
    		const category = get(categoryState);
    		return toDos.filter((toDo) => toDo.category === category);
    	},
    });
    toDoSelectorからtoDosとcateforyStateをロードし、現在のカテゴリに対応するtoDo値のみを返します.
    //CreateToDo.tsx
    import { useForm } from "react-hook-form";
    import { useRecoilValue, useSetRecoilState } from "recoil";
    import { categoryState, toDoState } from "./atoms";
    
    interface IForm {
    	toDo: string;
    }
    
    export default function CreateToDo() {
    	const setToDos = useSetRecoilState(toDoState);
    	const category = useRecoilValue(categoryState);
    	const { register, handleSubmit, setValue } = useForm<IForm>();
    	const handleValid = ({ toDo }: IForm) => {
    		setToDos((oldToDos) => [
    			{ text: toDo, id: Date.now(), category },
    			...oldToDos,
    		]);
    		setValue("toDo", "");
    	};
    	return (
    		<form onSubmit={handleSubmit(handleValid)}>
    			<input
    				{...register("toDo", {
    					required: "Please write a To Do",
    				})}
    				placeholder="Write a to do"
    			/>
    			<button>Add</button>
    		</form>
    	);
    }
    ToDoプロジェクトのコンポーネントを作成し、フォームのsubmit時にhandleValidでsetToDosを実行し、現在のカテゴリの場所にToDoを追加します.
    //ToDoList.tsx
    import React from "react";
    import { useRecoilState, useRecoilValue } from "recoil";
    import { Categories, categoryState, toDoSelector } from "../atoms";
    import CreateToDo from "./CreateToDo";
    import ToDo from "./ToDo";
    
    export default function ToDoList() {
    	const toDos = useRecoilValue(toDoSelector);
    	const [category, setCategory] = useRecoilState(categoryState);
    	const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
    		setCategory(event.currentTarget.value as Categories);
    	};
    	return (
    		<div>
    			<h1>To Dos</h1>
    			<hr />
    			<select value={category} onInput={onInput}>
    				<option value={Categories.TO_DO}>To Do</option>
    				<option value={Categories.DOING}>Doing</option>
    				<option value={Categories.DONE}>Done</option>
    			</select>
    			<CreateToDo />
    			{toDos?.map((toDo) => (
    				<ToDo key={toDo.id} {...toDo} />
    			))}
    		</div>
    	);
    }
    ToDolistの構成部品を表します.
    userRecoilValueにselector関数を追加し、対応する条件のtoDosを受け入れます.
    category()を使用して現在のcategoryStateのステータスを確認し、selectのオプションを選択するたびにsetCategory()を実行してcategoryStateのステータスを変更します.
    次いで、todosのコンテンツをtodoコンポーネントに順次送信する.
    //ToDo.tsx
    import React from "react";
    import { useSetRecoilState } from "recoil";
    import { Categories, IToDo, toDoState } from "./atoms";
    
    export default function ToDo({ text, category, id }: IToDo) {
    	const setToDos = useSetRecoilState(toDoState);
    	const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    		const {
    			currentTarget: { name },
    		} = event;
    		setToDos((oldToDos) => {
    			const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id);
    			const newToDo = { text, id, category: name as IToDo["category"] };
    			return [
    				...oldToDos.slice(0, targetIndex),
    				newToDo,
    				...oldToDos.slice(targetIndex + 1),
    			];
    		});
    	};
    	return (
    		<li>
    			<span>{text}</span>
    			{category !== Categories.DOING && (
    				<button name={Categories.DOING} onClick={onClick}>
    					Doing
    				</button>
    			)}
    			{category !== Categories.TO_DO && (
    				<button name={Categories.TO_DO} onClick={onClick}>
    					To Do
    				</button>
    			)}
    			{category !== Categories.DONE && (
    				<button name={Categories.DONE} onClick={onClick}>
    					Done
    				</button>
    			)}
    		</li>
    	);
    }
    各ToDoを表示する構成部品にカテゴリを変更できます.
    該当するカテゴリボタンをクリックしてonClickのsetToDosを実行し、ToDoのカテゴリを変更します.
    setToDosで上記のように関数を使用すると、oldToDosにはToDo全体が読み込まれます.
    targetIndexはfindIndexを使用して現在のToDoの場所を検索します.
    新ToDoは,現在のToDo値のカテゴリのみを変更することにより生成を継続する.
    [...oldToDos.slice(0, targetIndex),
    	newToDo,
    ...oldToDos.slice(targetIndex + 1),]
    これは、現在のToDoをカットし、カテゴリを変更した新しいToDoを加えて返すコードです.category !== Categories.DOING &&が本物であれば、&&の後のコードを使用します.
    //App.tsx
    import ToDoList from "./ToDoList";
    
    export default function App() {
      return (
        <ToDoList />
      );
    }
    Dynamic Dependencies
    読み取り専用セレクタは、get依存関係に基づいてセレクタ値を計算する方法を提供します.これらの依存関係を更新すると、セレクタが再評価されます.依存関係は、セレクタを評価する際に実際に使用される原子またはセレクタに基づいて動的に決定されます.以前の依存関係の値に応じて、他の依存関係を動的に使用できます.Recoilは、現在のデータ・ストリーム・グラフィックスを自動的に更新し、選択者が現在の依存関係セットの更新のみを購読することを保証します.
    ソース
    例(set)
    上記の例ではselectorのgetのみが使用され、setも使用できます.
    Atomsの例を少し変更しました.
    //atoms.js
    import {atom, selector} from "recoil";
    
    export const counterAtom = atom({
    	key: "counter",
    	default : 0
    })
    
    export const countChange = selector({
    	key: "selector",
    	get: ({get}) => get(counterAtom) % 2 === 0,
    	set: ({set}, newValue) => set(counterAtom, newValue as number)
    });
    //ComponentValue.tsx
    import { useRecoilState, useRecoilValue } from "recoil";
    import { counterAtom, countChange } from "./atoms";
    
    export default function ComponentValue() {
    	const counter = useRecoilValue(counterAtom);
    	const [value, setCounter] = useRecoilState(countChange);
    	const onZero = () => setCounter(123);
    	return (
    		<div>
    			<p>Component Value Only</p>
    			<p>{counter}</p>
    			<input onClick={onZero} type="submit" value="zero" />
    			<p>{value ? "짝수" : "홀수"}</p>
    		</div>
    	)
    }
    const [value, setCounter] = useRecoilState(countChange)一般状態の確認と修正は、発生したものと同じです.
    上記の例では、ステータスをcountChangeに変更することは、直接AntaTomのステータスを変更することと変わりませんが、setがどのように動作しているかを確認してください.
    countChangeのgetに対応する値をロードする場合、countAtomの値が偶数の場合はtrue、奇数の場合falseとなります.
    setは、1番目のパラメータとしてオブジェクトに渡され、2番目のパラメータは新しい値に渡され、1番目のパラメータオブジェクトのsetを使用してステータス値を置き換えます.この場合、最初のパラメータはrecoil stateでなければなりません.2番目のパラメータは変更する値に対応します.
    Writeable Selectors
    双方向セレクタは、入力された値をパラメータとして受信し、変更をデータストリームパターンに沿って上流に伝播します.ユーザーは、セレクタを新しい値に設定したり、セレクタをリセットしたりすることができます.したがって、入力した値はセレクタが表すタイプと同じか、DefaultValueのリセット操作を表すオブジェクトです.
    デフォルトでは、この単純なセレクタは原子をパッケージして他のフィールドを追加します.設定とリセット操作によって上流原子に渡されます.ソース
    get関数のみが指定されている場合は、セレクタは読み取り専用であり、RecoilValue ReadOnlyオブジェクトを返します.setは、書き込み可能なRecoilstateオブジェクトも返します(使用可能な場合).ソース
    Reference
    https://nomadcoders.co/react-masterclass
    https://recoiljs.org/docs/introduction/getting-started
    https://levelup.gitconnected.com/a-new-state-management-for-react-recoil-53ad7480faa4
    https://medium.com/humanscape-tech/recoil-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-285b29135d8e
    https://ui.toast.com/weekly-pick/ko_20200616
    https://kkangil.github.io/2020/05/24/React-recoiljs-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0