Recoil公式ドキュメント 翻訳⑨ ガイド-非同期state・同期state

Recoilの公式ドキュメントをgoogle翻訳するとコードまで翻訳されてしまうのが面倒なのでQiitaにまとめてみます。

追々追加していきます。

[公式ドキュメント](https://recoiljs.org/docs/basic-tutorial/selectors)

## 目次

* **前書き**　[①](https://qiita.com/Daichi44/items/4236857dac4a3365f434)　[②](https://qiita.com/Daichi44/items/f6f8995a73387d104777)　[③](https://qiita.com/Daichi44/items/b46d9659c8fcfad2c227)　[④](https://qiita.com/Daichi44/items/3356aaeb7a387b520621)
* **基本チュートリアル**　[⑤](https://qiita.com/Daichi44/items/ee1ced6040d06e33e165)　[⑥](https://qiita.com/Daichi44/items/d6c2472043b473c9819d)　[⑦](https://qiita.com/Daichi44/items/af3d6363ac3ce3e7af5a)
* **ガイド**
 * [⑧非同期データクエリ](https://qiita.com/Daichi44/items/47e9ac34c61c35531abb)
 * <font color="orange">**⑨非同期state・同期state**</font>
 * [⑩Stateの永続性](https://qiita.com/Daichi44/items/45093185eb06fb4e1171)
* **APIリファレンス**
 * **Core**　[⑪](https://qiita.com/Daichi44/items/b440f33d3831b86c62b9)　[⑫](https://qiita.com/Daichi44/items/0a9b9af69dddddbcd7e2)　[⑬](https://qiita.com/Daichi44/items/13912706763c22f9cbe9)　[⑭](https://qiita.com/Daichi44/items/812aa5ebf149c849e108)　[⑮](https://qiita.com/Daichi44/items/2ec591ec3e952f1784c3)　[⑯](https://qiita.com/Daichi44/items/f78a2ce0cfc01de354c5)　[⑰](https://qiita.com/Daichi44/items/ab0e98e0a48d0a59bdb4)　[⑱](https://qiita.com/Daichi44/items/a887cfa33260136b1d68)　[⑲](https://qiita.com/Daichi44/items/679030df40e81a608be5)　[⑳](https://qiita.com/Daichi44/items/f682baf85f0e2673abfc)　[㉑](https://qiita.com/Daichi44/items/1c38716dc0738da3bc02)　[㉒](https://qiita.com/Daichi44/items/84a7218e76f62d41d0b7)　[㉓](https://qiita.com/Daichi44/items/de7e39f80cd1fdce8b7c)　[㉔](https://qiita.com/Daichi44/items/eda197468b0204349d5f)　[㉕](https://qiita.com/Daichi44/items/0bbe5d6643462b55df11)　[㉖](https://qiita.com/Daichi44/items/0a276eb144f443a72efd)　[㉗](https://qiita.com/Daichi44/items/78292a5c30b1b1e4813f)
 * **実用(utils)**　[㉘](https://qiita.com/Daichi44/items/c6046e45ba7447a64aed)　[㉙](https://qiita.com/Daichi44/items/f552dfda4765596d83d7)　[㉚](https://qiita.com/Daichi44/items/7e568ccfb6d2e504de13)　[㉛](https://qiita.com/Daichi44/items/6973c6d140b697b2c97e)　[㉜](https://qiita.com/Daichi44/items/b929ff5376d0672dafa0)　[㉝](https://qiita.com/Daichi44/items/e9720f6ceb7cea57b2ce)　[㉞](https://qiita.com/Daichi44/items/c9bb722022a003d72aa9)　[㉟](https://qiita.com/Daichi44/items/ca11fcef04d764ef13a8)

※[全目次](https://qiita.com/Daichi44/items/ae631b34ac50fb9bbd1d#%E5%85%A8%E7%9B%AE%E6%AC%A1)は一番下にあります

***

# 非同期state・同期state

***

### *重要なお知らせ*
*このAPIは現在開発中であり、今後変更される予定です。お楽しみに...*

***

Recoil [atoms](https://recoiljs.org/docs/api-reference/core/atom) は局所的な応用stateを表します。
アプリケーションは、RESTful APIなどを介してリモートまたはサーバー側のstateを持っている場合もあります。
リモートstateをRecoil atomと同期することを検討してください。これにより、`useRecoilState()`hookを使用してReactコンポーネントからstateに簡単にアクセスしたり、そのstateを他の派生state selectorのデータフローグラフへの入力として使用したりできます。
[データベースまたはサーバーに読み取り専用データを問い合わせる](https://qiita.com/Daichi44/items/47e9ac34c61c35531abb)場合は、非同期selectorの使用を検討してください。

## ローカルStateの例
この例では、フレンドステータスをローカルstateとしてのみ提供します。

```React
const currentUserIDState = atom({
  key: 'CurrentUserID',
  default: null,
});

function CurrentUserInfo() {
  const [currentUserID] = useRecoilState(currentUserIDState);
  return <div>Current User: {currentUserID}</div>;
}
```

## サーバからstateを同期
リモートstateの非同期変更を登録し、それに合わせてatom値を更新することができます。これは、通常 React [`useEffect()`](https://reactjs.org/docs/hooks-reference.html#useeffect)hookまたはその他の一般的なライブラリを使用して実行できます。

```React
function CurrentUserIDSubscription() {
  const setCurrentUserID = useSetRecoilState(currentUserIDState);

  useEffect(() => {
    RemoteStateAPI.subscribeToCurrentUserID(setCurrentUserID);
    // Specify how to cleanup after this effect
    return function cleanup() {
      RemoteServerAPI.unsubscribeFromCurrentUserID(setCurrentUserID);
    };
  }, []);

  return null;
}

function MyApp() {
  return (
    <RecoilRoot>
      <CurrentUserIDSubscription />
      <CurrentUserInfo />
    </RecoilRoot>
  );
}
```

1つの場所で複数のatomの同期を処理する場合は、[State Persistence](https://recoiljs.org/docs/guides/persistence)パターンも使用できます。

## 双方向同期
stateを同期して、ローカルの変更がサーバー上で更新されるようにすることもできます。
これは単純化した例です。フィードバックのループを避けるように注意してください。

```React
function CurrentUserIDSubscription() {
  const [currentUserID, setCurrentUserID] = useRecoilState(currentUserIDState);
  const knownServerCurrentUserID = useRef(currentUserID);

  // Subscribe server changes to update atom state
  useEffect(() => {
    function handleUserChange(id) {
      knownServerCurrentUserID.current = id;
      setCurrentUserID(id);
    }

    RemoteStateAPI.subscribeToCurrentUserID(handleUserChange);
    // Specify how to cleanup after this effect
    return function cleanup() {
      RemoteServerAPI.unsubscribeFromCurrentUserID(handleUserChange);
    };
  }, [knownServerCurrentUserID]);

  // Subscribe atom changes to update server state
  useEffect(() => {
    if (currentUserID !== knownServerCurrentUserID.current) {
      knownServerCurrentID.current = currentUserID;
      RemoteServerAPI.updateCurrentUser(currentUserID);
    }
  }, [currentUserID, knownServerCurrentUserID.current]);

  return null;
}
```

## パラメータを使用したstateの同期
[atomFamily](https://recoiljs.org/docs/api-reference/utils/atomFamily)ヘルパーを使用して、パラメータに基づいてローカルstateを同期することもできます。
このhookの例の呼び出しごとにサブスクリプションが作成されるため、冗長な使用を避けるように注意してください。

```React
const friendStatusState = atomFamily({
  key: 'Friend Status',
  default: 'offline',
});

function useFriendStatusSubscription(id) {
  const setStatus = useSetRecoilState(friendStatusState(id));

  useEffect(() => {
    RemoteStateAPI.subscribeToFriendStatus(id, setStatus);
    // Specify how to cleanup after this effect
    return function cleanup() {
      RemoteServerAPI.unsubscribeFromFriendStatus(id, setStatus);
    };
  }, []);
}
```

# データフローグラフ
リモートstateを表すためにatomを使用する利点は、他の派生stateの入力として使用できることです。
次の例では、現在のサーバのstateに基づいて、現在のユーザと友人のリストを表示します。サーバが現在のユーザを変更すると、リスト全体が再描画されます。友人のステータスだけを変更すると、そのリストエントリだけが再描画されます。リスト項目をクリックすると、現在のユーザがローカルに変更され、サーバのstateが更新されます。

```React
const userInfoQuery = selectorFamily({
  key: 'UserInfoQuery',
  get: userID => async ({get}) => {
    const response = await myDBQuery({userID});
    if (response.error) {
      throw response.error;
    }
    return response;
  },
});

const currentUserInfoQuery = selector({
  key: 'CurrentUserInfoQuery',
  get: ({get}) => get(userInfoQuery(get(currentUserIDState)),
});

const friendColorState = selectorFamily({
  key: 'FriendColor',
  get: friendID => ({get}) => {
    const [status] = get(friendStatusState(friendID));
    return status === 'offline' ? 'red' : 'green';
  },
});

function FriendStatus({friendID}) {
  useFriendStatusSubscription(friendID);
  const [status] = useRecoilState(friendStatusState(friendID));
  const [color] = useRecoilState(friendColorState(friendID));
  const [friend] = useRecoilState(userInfoQuery(friendID));
  return (
    <div style={{color}}>
      Name: {friend.name}
      Status: {status}
    </div>
  );
}

function CurrentUserInfo() {
  const {name, friendList} = useRecoilValue(currentUserInfoQuery);
  const setCurrentUserID = useSetRecoilState(currentUserIDState);
  return (
    <div>
      <h1>{name}</h1>
      <ul>
        {friendList.map(friendID =>
          <li key={friend.id} onClick={() => setCurrentUserID(friend.id)}>
            <React.Suspense fallback={<div>Loading...</div>}>
              <FriendStatus friendID={friendID} />
            </React.Suspense>
          </li>
        )}
      </ul>
    </div>
  );
}

function MyApp() {
  return (
    <RecoilRoot>
      <ErrorBoundary>
        <React.Suspense fallback={<div>Loading...</div>}>
          <CurrentUserIDSubscription />
          <CurrentUserInfo />
        </React.Suspense>
      </ErrorBoundary>
    </RecoilRoot>
  );
}
```

***

### 参考サイト
・[公式ドキュメント](https://recoiljs.org/docs/basic-tutorial/selectors)
・[React ドキュメント useEffect()](https://reactjs.org/docs/hooks-reference.html#useeffect)
・[みらい翻訳](https://miraitranslate.com/trial)

***

>## 全目次
* **前書き**
 * [①動機](https://qiita.com/Daichi44/items/4236857dac4a3365f434)
 * [②コアコンセプト](https://qiita.com/Daichi44/items/f6f8995a73387d104777)
 * [③インストール](https://qiita.com/Daichi44/items/b46d9659c8fcfad2c227)
 * [④入門](https://qiita.com/Daichi44/items/3356aaeb7a387b520621)
* **基本チュートリアル**
 * [⑤概要](https://qiita.com/Daichi44/items/ee1ced6040d06e33e165)
 * [⑥Atoms](https://qiita.com/Daichi44/items/d6c2472043b473c9819d)
 * [⑦Selectors](https://qiita.com/Daichi44/items/af3d6363ac3ce3e7af5a)
* **ガイド**
 * [⑧非同期データクエリ](https://qiita.com/Daichi44/items/47e9ac34c61c35531abb)
 * <font color="orange">**⑨非同期state・同期state**</font>
 * [⑩Stateの永続性](https://qiita.com/Daichi44/items/45093185eb06fb4e1171)
* **APIリファレンス**
 * **Core** <br>
   •　[⑪ \<RecoilRoot /\>](https://qiita.com/Daichi44/items/b440f33d3831b86c62b9) <br>
   •　**State** <br>
   　・　[⑫`atom()`](https://qiita.com/Daichi44/items/0a9b9af69dddddbcd7e2) <br>
   　・　[⑬`selector()`](https://qiita.com/Daichi44/items/13912706763c22f9cbe9) <br>
   　・　[⑭Loadable](https://qiita.com/Daichi44/items/812aa5ebf149c849e108) <br>
   　・　[⑮`useRecoilState()`](https://qiita.com/Daichi44/items/2ec591ec3e952f1784c3) <br>
   　・　[⑯`useRecoilValue()`](https://qiita.com/Daichi44/items/f78a2ce0cfc01de354c5) <br>
   　・　[⑰`useSetRecoilState()`](https://qiita.com/Daichi44/items/ab0e98e0a48d0a59bdb4) <br>
   　・　[⑱`useResetRecoilState()`](https://qiita.com/Daichi44/items/a887cfa33260136b1d68) <br>
   　・　[⑲`useRecoilValueLoadable()`](https://qiita.com/Daichi44/items/679030df40e81a608be5) <br>
   　・　[⑳`useRecoilStateLoadable()`](https://qiita.com/Daichi44/items/f682baf85f0e2673abfc) <br>
   　・　[㉑`isRecoilValue()`](https://qiita.com/Daichi44/items/1c38716dc0738da3bc02) <br>
   •　**Snapshots** <br>
   　・　[㉒Snapshot](https://qiita.com/Daichi44/items/84a7218e76f62d41d0b7) <br>
   　・　[㉓`useRecoilTransactionObserver()`](https://qiita.com/Daichi44/items/de7e39f80cd1fdce8b7c) <br>
   　・　[㉔`useRecoilSnapshot()`](https://qiita.com/Daichi44/items/eda197468b0204349d5f) <br>
   　・　[㉕`useGotoRecoilSnapshot()`](https://qiita.com/Daichi44/items/0bbe5d6643462b55df11) <br>
   •　[㉖`useRecoilCallback()`](https://qiita.com/Daichi44/items/0a276eb144f443a72efd) <br>
   •　**雑録(Misc)** <br>
   　・　[㉗`useRecoilBridgeAcrossReactRoots()`](https://qiita.com/Daichi44/items/78292a5c30b1b1e4813f) <br>
 * **実用(utils)** <br>
   •　[㉘`atomFamily()`](https://qiita.com/Daichi44/items/c6046e45ba7447a64aed) <br>
   •　[㉙`selectorFamily()`](https://qiita.com/Daichi44/items/f552dfda4765596d83d7) <br>
   •　[㉚`constSelector()`](https://qiita.com/Daichi44/items/7e568ccfb6d2e504de13) <br>
   •　[㉛`errorSelector()`](https://qiita.com/Daichi44/items/6973c6d140b697b2c97e) <br>
   •　[㉜`waitForAll()`](https://qiita.com/Daichi44/items/b929ff5376d0672dafa0) <br>
   •　[㉝`waitForAny()`](https://qiita.com/Daichi44/items/e9720f6ceb7cea57b2ce) <br>
   •　[㉞`waitForNone()`](https://qiita.com/Daichi44/items/c9bb722022a003d72aa9) <br>
   •　[㉟`noWait()`](https://qiita.com/Daichi44/items/ca11fcef04d764ef13a8) <br>