テックキャンプ卒の弱々エンジニア日記

エンジニアとして働く中での学びをちょっとでも記録していきます。

bulletproof-reactに則った処理の流れ

こんにちは、Tochiです。 ようやく新しい環境にも慣れてきた今日この頃です。

社内で適用しているbulletproof-reactの使い方が徐々にわかってきたので 復習を兼ねてアウトプットしていきたいと思います。

bulletproof-reactとは

bulletproof-reactはReactアプリケーションのベストプラクティスの宝庫です github.com

概要としては、こちらが非常にわかりやすいので参考にしてください

zenn.dev

今回作りたいもの

今回作りたいものとしては、 シンプルな投稿の一覧画面と検索ボックスを作っていく感じになります。

本記事ではあくまでbulletproof-reactに焦点を当てたいので 細かい実装に関しては割愛しています。

一覧について

まず一覧の大まかな作りとしては以下のような形です。

設計方針 一覧

ディレクトリ構成はbulletproof-reactに則って、features/posts/配下に下記のような形で配置しています。 必要に応じて、utilsディレクトリなんかも置いてもいいかもです。

features/posts/
|
+-- api                         # exported API request declarations and api hooks related to a specific feature
|
+-- components        # shared components used across the entire application
|
+-- features               # feature based modules
|
+-- hooks                  # shared hooks used across the entire application
|
+-- providers           # all of the application providers
|
+-- types                 # base types used across the application
|
+-- index.ts

featuresはルーティングベースでディレクトリ分けを行うので、 もし/usersに関しての機能をまとめたい場合は、同じように/features/usersディレクトリを作成します。

なお、GraphQLを利用を今回は前提としています(Apolloを利用しています)が、この辺は割愛しますのでご了承ください。

コードとその説明

1つづつ解説していきます。

まず、リクエストするGraphQLのクエリを定義していきます。 このクエリは、features/posts/posts.graphqlで定義します。

query Posts {
  posts {
    nodes {
      id
      name
    }
  }
}

このGraphQLの定義は、GraphQL Code Generatorを使うことで /lib/graphql/index.tsにコードが生成されます。(usePostsQueryなどが使えるようになります)

次にProviderコンポーネントを定義します。 今回はpostsデータは一覧コンポーネントと検索コンポーネントにまたがって利用したいものなので 定義されたフックを用いてProviderコンポーネント使う必要があります。 providers/posts.tsxに定義していきます。

# typeの定義とは割愛してます。


const PostContext = createContext<PostContext>({....})

export const PostsProvider = ({children}: Props) => {

   const [posts, setPosts] = useState([])
   const [refetch, { data, loading }] = usePostsLazyQuery()

  useEffect(() => {
    if (queryLoading) return

    if (data) {
      setPosts(data.posts)
    }

    setLoading(false)
  }, [data,loading])

 return (
    <PostsContext.Provider
      value={{
       posts,
        refetch: handleRefetch
      }}
    >
      {children}
    </PostsContext.Provider>
  )
}

export usePostsContext() = () => useContext(PostsContext)

ここで定義したProviderコンポーネント/app/posts/page.tsxで利用します イメージこんな感じです↓

<PostsProvider>
    <SearchForm />
    <Table/ >
</PostsProvider>

こうすることで、SearchFormコンポーネントでも投稿データを受け取れます。(いちいちリクエストを送らずにすみます)

ここまできたらあとはcomponents/table.tsxを作ってあげるだけです。

export const Table = () => {
  const { data, loading } = usePostsContext();

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      {data.posts.map(post => (
        <div key={post.id}>{post.name}</div>
      ))}
    </div>
  );
};  

以上、ざっくりですがこんな感じでコンポーネントとロジックを定義していってあげます。

補足

検索フォームについてはhooksを使いますが基本的な考え方は同じです。 超ざっくりイメージd

設計方針 検索

まとめ

bulletproof-reactを使うと、各ディレクトリの役割が明確に決まっているために どこに何が書かれているかがわかりやすくコードを非常に書きやすいというメリットを感じました。

また、自由度が高いために生じる複雑性といったものもルールがあるがゆえに シンプルに記載するような意識が働いてコードが複雑化することも防いでくれるのではないかと思いました。