SYSTEM: ONLINE
VER. ...
SLEET LOG
SLEET'S LOG
/2022年12月31日/2 MIN READ

如何通过 React 钩子取代容器组件

React

容器组件是提供、创建和持有数据并服务于子组件的组件,其唯一的工作是处理数据,而将展示 UI 的工作交给展示组件。而处理数据的工作的逻辑不止能直接写在容器组件中,其实也可以被封装成一个 hooks,再被容器组件调用,从而使这部分逻辑抽离、语义化并易于维护。

以下是一个容器组件的例子。它通过 useEffect 获取文章数据,期间会维护isLoadingerrorposts三个状态变量,再通过这三个变量控制展示组件的渲染。

tsx
// src/components/PostContainer.tsx import { useEffect, useState } from "react"; import { ISinglePost } from "./types"; import Posts from "./Posts"; export default function PostContainer() { const [posts, setPosts] = useState<ISinglePost[] | null>(null); const [isLoading, setIsLoading] = useState<Boolean>(false); const [error, setError] = useState<unknown>(); useEffect(() => { (async () => { try { setIsLoading(true); const resp = await fetch("https://jsonplaceholder.typicode.com/posts"); const data = await resp.json(); setPosts(data.filter((post: ISinglePost) => post.userId === 1)); setIsLoading(false); } catch (err) { setError(err); setIsLoading(false); } })(); }, []); return isLoading ? ( <span>Loading... </span> ) : posts ? ( <Posts posts={posts} /> ) : ( <span>{JSON.stringify(error)}</span> ); }

除了能直接在组件里获取文章数据,其实我们也可以将获取文章数据这部分的逻辑抽离为一个 hooks。同样维护isLoadingerrorposts三个状态变量,但是最后是将这些变量作为函数的返回值,利用函数闭包特性,将三个变量暴露给容器组件使用。

tsx
// src/hooks/usePosts.ts import { useEffect, useState } from "react"; import { ISinglePost } from "./types"; export default function usePosts() { const [posts, setPosts] = useState<ISinglePost[] | null>(null); const [isLoading, setIsLoading] = useState<Boolean>(false); const [error, setError] = useState<unknown>(); useEffect(() => { (async () => { try { setIsLoading(true); const resp = await fetch("https://jsonplaceholder.typicode.com/posts"); const data = await resp.json(); setPosts(data.filter((post: ISinglePost) => post.userId === 1)); setIsLoading(false); } catch (err) { setError(err); setIsLoading(false); } })(); }, []); return { isLoading, posts, error, }; }

容器组件使用usePostshooks 的示例如下

tsx
// src/components/PostsContainer.tsx import { ISinglePost } from "./types"; import usePosts from "@/hooks/usePosts"; import SinglePost from "./SinglePost"; export default function Posts(props: { posts: ISinglePost[] }) { const { isLoading, posts, error } = usePosts(); return ( <ul style={{ display: "flex", flexDirection: "column", alignItems: "center", }} > {isLoading ? ( <span>Loading...</span> ) : posts ? ( posts.map((post: ISinglePost) => <SinglePost {...post} />) ) : ( <span>{JSON.stringify(error)}</span> )} </ul> ); }

参考 React 中的关注点分离——如何使用容器组件和展示组件

Article Index