あなたの反応アプリで州を管理する方法


あなたの反応アプリの状態を管理するのと同じように使用されていませんuseState or useReducer .
だけでなく、さまざまな種類の状態がありますが、しばしば、各種類の管理方法の多くの方法があります.どちらを選びますか.
このガイドでは、我々はあなたが最も効果的な方法でそれらを管理する方法を知っていない可能性がありますあなたの反応アプリの状態のいくつかの種類を明らかにします.
管理する4つの反応状態
我々が我々のアプリケーションの状態について話すとき、どんなタイプの州が実際に重要であるかについて明確であることは重要です.
適切にあなたの反応アプリで管理するために必要な状態の4つの主要なタイプがあります:
ローカル州
グローバルステート
サーバ状態
URLの状態
これらのそれぞれを詳しく説明しましょう.
ローカル(UI)状態-ローカル状態は、我々が1つまたは他のコンポーネントで管理するデータです.
ローカル状態は、最も頻繁に使用してuseState フック.
たとえば、フォームコンポーネントを表示または非表示にするか、フォームコンポーネントの値を追跡するには、フォームが無効になってフォームの入力の値をフォームフォームなどの値を追跡する必要があります.
*グローバル(UI)状態*-グローバル状態は、複数のコンポーネント間で管理するデータです.
グローバル状態は、私たちが取得し、我々のアプリのどこにでもデータを更新したり、少なくとも複数のコンポーネントで必要です.
グローバル状態の一般的な例は認証されたユーザー状態です.ユーザーが我々のアプリにログインしている場合、それは取得し、我々のアプリケーション全体のデータを変更する必要があります.
時々、我々が考えるべきであるローカルは、グローバルになるかもしれません.
サーバー状態-私たちのUI状態と統合されなければならない外部サーバーから来るデータ.
サーバーの状態は、単純な概念ですが、すべてのローカルおよびグローバルUIの状態と一緒に管理するのは難しいことができます.
ロードとエラー状態を含む外部サーバーからデータを取得または更新するたびに、管理されなければならないいくつかの状態があります.
幸いにもSWRのようなツールがあり、反応のクエリは、サーバーの状態を管理するように簡単になります.
URL状態-パス名とクエリパラメータを含むURLに存在するデータ.
URL状態はしばしば状態のカテゴリーとして欠落していますが、それは重要なものです.
多くの場合、我々のアプリケーションの多くの主要な部分はURL州にアクセスすることに頼ります.そのスラッグやIDに基づいて投稿を取得することなく、ブログを構築することを想像してみてください!
間違いなく、私たちが特定することができた状態のより多くの部分がありますが、これらはあなたが構築する大部分のアプリケーションのために集中する価値がある主要なカテゴリーです.
ローカル州を管理する方法
ローカル状態はおそらく、管理するためのコア反応ライブラリに非常に多くのツールが組み込まれていることを考慮して反応する管理の最も簡単な種類です.useState あなたのコンポーネントの状態を管理するために到達する最初のツールです.
プリミティブとオブジェクトの値を含む任意の有効なデータ値を受け入れることができます.さらに、setset関数は、コールバック関数として他のコンポーネントに渡されることができますuseCallback ).
import { useState } from "react";

function Layout() {
  const [isSidebarOpen, setSidebarOpen] = useState(false);

  return (
    <>
      <Sidebar isSidebarOpen={isSidebarOpen} closeSidebar={() => setSidebarOpen(false)} />
      {/* ... */}
    </>
  );
}
useReducer はローカルまたはグローバル状態のどちらかに使用できる別のオプションです.それは多くの点で似ているuseState フードの下では、初期状態の代わりに、それは還元器を受け入れます.
利益useReducer というのは、reducer よりも全体的に動的になるuseState .
あなたの利益を見ることができますuseReduceruseState この投票追跡例で.状態を更新するために必要なのはコールバック関数dispatch 新しい状態自体ではなく、文字列(その後、還元子に渡される).
const initialState = { votes: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'upvote':
      return {votes: state.votes + 1};
    case 'downvote':
      return {votes: state.votes - 1};
    default:
      throw new Error();
  }
}

function VoteCounter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Current Votes: {state.votes}
      <button onClick={() => dispatch({type: 'upvote'})}>Upvote</button>
      <button onClick={() => dispatch({type: 'downvote'})}>Downvote</button>
    </>
  );
}
グローバル状態を管理する方法
複数のコンポーネント間で状態を管理しようとすると、物事は少しトリッキーになります.
あなたのアプリケーションの“リフト状態”のようなパターンを通過し、コンポーネントからあなたの状態を更新するためにコールバックを渡すポイントは、多くの小道具とたくさんの小道具につながる.
あなたのアプリケーションの基本的にどこからコンポーネントの状態を更新したい場合はどうしますか?あなたは世界的な状態になります.
しかし、それを管理するためには、サードパーティの解決策を選ぶべきです.多くの開発者は、それらの状態を管理するためのコンテキストAPIのような組み込みの反応機能を使用する傾向があります.
明確に:コンテキストAPIは、状態管理ソリューションではありません.それは小道具の穴のような問題を避けるための方法です(それを必要としないコンポーネントの小道具の束を作成する)が、それはそれを更新していない状態を読み取るためにのみ有用です.
グローバル状態管理のコンテキストを使用しない理由は、それが動作する方法です.コンテキストの既定の動作は、プロップとして提供される値が変更された場合、すべての子要素を再描画することです.
例えば、結合するのは悪い習慣ですuseReducer and useContext :
function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <StateProvider.Provider value={{ state, dispatch }}>
      <ComponentA />
      <ComponentB />
      <ComponentC />
    </StateProvider.Provider>
  )
}
多くの場合、すべての子が消費されないか、またはグローバル状態に依存していない可能性があるので、グローバル状態更新に応答してすべての子を更新する必要はありません.彼らの小道具や状態の変更を再表示するだけです.
あなたの世界的な状態を管理するために、Zustandのような試みられて、テストされたサードパーティ製の図書館、Jotaiと反動のために手を伸ばしてください.
ズステートジョタイタイ
ツールキットライブラリ
reduxも素晴らしいですが、Reduxツールキットを使用して起動することを確認します.
Zustandのようなライブラリの利点は、それが小さくて、グローバルな状態全体をカスタムフックにし、状態を読んだり更新したりするには、このフックをコンポーネントに呼び出すだけです.
Zustandを使用するにはrun npm install zustand . その後、専用のストアファイルまたはフォルダを作成し、あなたのストアを作成します
import create from 'zustand'

const useStore = create(set => ({
  votes: 0,
  upvote: () => set(state => ({ vote: state.votes + 1 })),
  downvote: () => set(state => ({ vote: state.votes - 1 })),
}))

function VoteCounter() {
  const { votes, upvote, downvote } = useStore();

  return (
    <>
      Current Votes: {votes}
      <button onClick={upvote}>Upvote</button>
      <button onClick={downvote}>Downvote</button>
    </>
  );
}
私がReduxのようなライブラリの上にZustandを使用することをお勧めする1つの大きな理由は、それがあなたにboilerplateとアクション、減速器の概念上のオーバーヘッドなしで必要とするすべての機能を与えるということです.
さらに、コンテキストプロバイダでコンポーネントをラップする必要はありません.ちょうどインストールしてください!
サーバー状態を管理する方法
サーバーの状態を管理するために一見挑戦することができます.
最初は、データを取得してページに表示する必要があるようです.しかし、あなたがデータを待っている間、ローディングスピナーを表示する必要があります.次に、エラーを処理し、ユーザーにそれらを表示する必要があります.
ネットワークエラーが発生したらどうなりますか?データが変更されないならば、私のユーザーがホームページを訪問するたびに、私は本当に私のサーバーを打つ必要がありますか?を追加する必要がありますかuseState and useEffect すべてのコンポーネントで私のデータを取得したいですか?
これを修正するには、データをフェッチする大きなライブラリがあります.
SWRおよびResponse問い合わせライブラリ
それらはAPIからデータを取得したり変更したりするのに便利なフックを与えるだけでなく、必要なすべての状態を追跡し、データをキャッシュします.
クライアント上のAPIからユーザプロファイルを取得する例を示します.コールuseSWR データを要求するエンドポイントを指定します.fetcher 機能とuseSWR 我々両方を与えるdata and error 状態.
import useSWR from 'swr'

const fetcher = url => fetch(url).then(r => r.json())

function User() {
  const { data, error } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>

  return <div>hello {data.name}!</div>
}
SWRは非常に簡単に要求を管理することができますし、私たちのコンポーネントは、多くの見てより良い.
また、何度も何度も同じ操作を実行している場合は、使用するuseSWR あなた自身のカスタムフックであなたのアプリケーション全体を再利用する.
function useUser (id) {
  const { data, error } = useSWR(`/api/user/${id}`, fetcher)

  return {
    user: data,
    isLoading: !error && !data,
    isError: error
  }
}

function Avatar ({ id }) {
  const { user, isLoading, isError } = useUser(id)

  if (isLoading) return <Spinner />
  if (isError) return <Error />

  return <img src={user.avatar} />
}
そして最後に、グローバルオプションを提供することができますuseSWR , あなたを含むfetcher 関数は(ので、すべての時間でそれを渡す必要がないだけでなく)回の回数を再度データを再度エラーを取得します.
import useSWR, { SWRConfig } from 'swr'

function Admin () {
  // no need to pass in the fetcher function
  const { data: courses } = useSWR('/api/courses')
  const { data: orders } = useSWR('/api/orders')
  const { data: users } = useSWR('/api/users')

  // ...
}

function App () {
  return (
    <SWRConfig 
      value={{
        errorRetryCount: 2, 
        errorRetryInterval: 5000,
        fetcher: (resource, init) => fetch(resource, init).then(res => res.json())
      }}
    >
      <Admin />
    </SWRConfig>
  )
}
これはSWRライブラリの利点の単なる味であり、応答クエリは、多くの利点としてだけではなく、場合を与える.
必ずサーバー状態を管理するためにどちらかを使用してください.それはあなたの人生はとても簡単になります.
URLで状態を管理する方法
場合は、次のようなフレームワークを使用している場合は、肯定的な注意事項については難しいURLを終了するには、URLの状態は、主に既に管理されます.JSまたは現在のバージョンの反応ルータ.
URLの状態は、通常私たちの場所、歴史、パス名について必要なすべての情報を与えるカスタムフックを介して管理するために、非常に簡単です.
あなたが反応ルータを使用しているならば、あなたはuseHistory またはUSELocation ``です.
`
import { useHistory, useLocation } from 'react-router-dom';

function BlogPost() {
const history = useHistory();
console.log("you are here: ", history.location);

const location = useLocation();

console.log('your pathname is: , location.pathname);

// ...
}

Additionally, if you have any route parameters that you need to use, for example to fetch data based off of, you can use the
useParams ` hook.

`
import { useParams } from 'react-router-dom';

function ChatRoom() {
const { roomId } = useParams();
const { chatRoom, isLoading, isError } = useChatRoom(roomId);

// ...
}
If you are using Next.js, almost everything can access directly from calling useRouter.

function Orders() {
const router = useRouter();
console.log('the entire url is: ', router.asPath);
console.log('your current route is: ', router.pathname);
console.log('your query params are: ', router.query);

function handleSubmit(item) {
setQuery("");
// push to new route
router.push(item.href);
closeDropdown();
}

// ...
}

`