useDLE() - [D]ata [L]oading [E]rror
高性能的异步数据渲染,不会过度获取,并附带获取的元数据。
如果你无法使用 suspense,useDLE() 与 useSuspense() 完全一样,只是它会返回 [D]ata [L]oading [E]rror(数据、加载、错误)这几个值。
useDLE() 会响应数据变更,只在必要时重新渲染。
用法
ProfileResource
ProfileList
import React from 'react'; import { useDLE } from '@data-client/react'; import { ProfileResource } from './ProfileResource'; function ProfileList(): React.JSX.Element { const { data, loading, error } = useDLE(ProfileResource.getList); if (error) return <div>Error {`${error.status}`}</div>; if (loading || !data) return <Loading />; return ( <div> {data.map(profile => ( <div className="listItem" key={profile.pk()}> <Avatar src={profile.avatar} /> <div> <h4>{profile.fullName}</h4> <p>{profile.bio}</p> </div> </div> ))} </div> ); } render(<ProfileList />);
结果
Store▶
行为
| 过期状态 | 获取 | Data | Loading | Error | 条件 |
|---|---|---|---|---|---|
| Invalid | 是1 | undefined | true | false | 不在 store 中、删除、失效、invalidIfStale |
| Stale | 是1 | 反规范化后的值 | false | false | (首次渲染、参数变化)且 expiry < now |
| Valid | 否 | 反规范化后的值 | false | 可能2 | 获取完成 |
| 否 | undefined | false | false | 第二个参数传入 null |
备注
- 相同的获取会被自动去重
- 硬错误会被 Error Boundaries 捕获
React Native
使用 React Navigation 时,如果数据被视为过时,useDLE() 会在获得焦点时触发获取。
条件依赖
将 null 作为任意 Data Client hook 的第二个参数,表示“什么都不做”。
// todo could be undefined if id is undefined
const todo = useDLE(TodoResource.get, id ? { id } : null);
类型
- 类型
- 使用泛型
function useDLE(
endpoint: ReadEndpoint,
...args: Parameters<typeof endpoint> | [null]
): {
data: Denormalize<typeof endpoint.schema>;
loading: boolean;
error: Error | undefined;
};
function useDLE<
E extends EndpointInterface<
FetchFunction,
Schema | undefined,
undefined
>,
Args extends readonly [...Parameters<E>] | readonly [null],
>(
endpoint: E,
...args: Args
): {
data: DenormalizeNullable<typeof endpoint.schema>;
loading: boolean;
error: Error | undefined;
};
示例
详情
ProfileResource
ProfileDetail
import React from 'react'; import { useDLE } from '@data-client/react'; import { ProfileResource } from './ProfileResource'; function ProfileDetail(): React.JSX.Element { const { data: profile, loading, error, } = useDLE(ProfileResource.get, { id: 1 }); if (error) return <div>Error {`${error.status}`}</div>; if (loading || !profile) return <Loading />; return ( <div className="listItem"> <Avatar src={profile.avatar} /> <div> <h4>{profile.fullName}</h4> <p>{profile.bio}</p> </div> </div> ); } render(<ProfileDetail />);
结果
Store▶
条件获取
传入 null 可以避免绑定和获取数据
▶Resources
▶PostWithAuthor
import { useDLE } from '@data-client/react'; import { PostResource, UserResource } from './Resources'; export default function PostWithAuthor({ id }: { id: string }) { const postDLE = useDLE(PostResource.get, { id }); if (postDLE.error) return <div>Error {`${postDLE.error.status}`}</div>; if (postDLE.loading || !postDLE.data) return <Loading />; const authorDLE = useDLE( UserResource.get, postDLE.data.userId ? { id: postDLE.data.userId, } : null, ); if (authorDLE.error) return <div>Error {`${authorDLE.error.status}`}</div>; if (authorDLE.loading || !authorDLE.data) return <Loading />; return <div>{authorDLE.data.username}</div>; }
嵌入数据
当 Entity 存储在嵌套结构中时,该结构会被保留。
▶api/Post
import { Entity, RestEndpoint, Collection } from '@data-client/rest'; export class PaginatedPost extends Entity { id = ''; title = ''; content = ''; static key = 'PaginatedPost'; } export const getPosts = new RestEndpoint({ path: '/post', searchParams: { page: '' }, schema: { results: new Collection([PaginatedPost]), nextPage: '', lastPage: '', }, });
▶ArticleList
import { useDLE } from '@data-client/react'; import { getPosts } from './api/Post'; export default function ArticleList({ page }: { page: string }) { const { data, loading, error } = useDLE(getPosts, { page }); if (error) return <div>Error {`${error.status}`}</div>; if (loading || !data) return <Loading />; const { results: posts, nextPage, lastPage } = data; return ( <div> {posts.map(post => ( <div key={post.pk()}>{post.title}</div> ))} </div> ); }
Github 表情回应
useDLE() 让我们可以在导航到任意 issue 页面的那一刻,声明式地获取其表情回应。这样即使表情回应尚未加载完成,也不会阻塞 issue 页面的显示。
通常更好的做法是把这类情况包裹在新的 Suspense 边界中。然而,我们使用的组件库 ant design 不允许这样做。
探索 github-app 示例