こんにちは、Tochiです。 ようやく新しい環境にも慣れてきた今日この頃です。
社内で適用しているbulletproof-reactの使い方が徐々にわかってきたので 復習を兼ねてアウトプットしていきたいと思います。
bulletproof-reactとは
bulletproof-reactはReactアプリケーションのベストプラクティスの宝庫です github.com
概要としては、こちらが非常にわかりやすいので参考にしてください
今回作りたいもの
今回作りたいものとしては、 シンプルな投稿の一覧画面と検索ボックスを作っていく感じになります。
本記事ではあくまで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を使うと、各ディレクトリの役割が明確に決まっているために どこに何が書かれているかがわかりやすくコードを非常に書きやすいというメリットを感じました。
また、自由度が高いために生じる複雑性といったものもルールがあるがゆえに シンプルに記載するような意識が働いてコードが複雑化することも防いでくれるのではないかと思いました。