跳到主要内容

useDLE() - [D]ata [L]oading [E]rror

高性能的异步数据渲染,不会过度获取,并附带获取的元数据。

如果你无法使用 suspense,useDLE() 与 useSuspense() 完全一样,只是它会返回 [D]ata [L]oading [E]rror(数据、加载、错误)这几个值。

useDLE() 会响应数据变更,只在必要时重新渲染。

用法​

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▶

行为​

过期状态获取DataLoadingError条件
Invalid是1undefinedtruefalse不在 store 中、删除、失效、invalidIfStale
Stale是1反规范化后的值falsefalse(首次渲染、参数变化)且 expiry < now
Valid否反规范化后的值false可能2获取完成
否undefinedfalsefalse第二个参数传入 null
备注
  1. 相同的获取会被自动去重
  2. 硬错误会被 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;
};

示例​

详情​

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 示例

更多演示