跳到主要内容

v0.9:DevTools、Resource.extend 与清理遗留 API

· 阅读需 8 分钟
Nathaniel Tucker
Creator of Reactive Data Client

本次发布聚焦于开发者体验:在开发模式下显示的 devtools 按钮、更好的 DevTools 历史记录,以及 controller.expireAll() 和 controller.fetchIfStale() 等新的 controller 方法。

Resource.extend() 提供了三种强大的方式来自定义 resource——添加新的 endpoint、覆盖已有的 endpoint,或从基础 endpoint 派生。

const UserResource = createResource({
path: '/users/:id',
schema: User,
}).extend('current', {
path: '/users/current',
});
迁移指南

破坏性变更:

Resource.extend() (自 0.5 起)​

Resource.extend() 提供了三种强大的方式来自定义 resource。 51b4b0d

添加新的 endpoint​

const UserResource = createResource({
path: '/users/:id',
schema: User,
}).extend('current', {
path: '/users/current',
});

覆盖已有的 endpoint​

const CachedArticleResource = ArticleResource.extend({
getList: {
dataExpiryLength: 10 * 60 * 1000, // 10 minutes
},
});

从基础 endpoint 派生​

const IssueResource = createResource({
path: '/issues/:id',
schema: Issue,
}).extend(Base => ({
byRepo: Base.getList.extend({
path: '/repos/:owner/:repo/issues',
}),
}));

Controller 方法​

controller.expireAll() (自 0.3 起)​

将所有匹配的响应标记为过时,在继续显示现有数据的同时触发后台重新获取。 #2802

// Mark all article lists as stale - they'll refetch when rendered
controller.expireAll(ArticleResource.getList);

这与会完全移除数据的 invalidateAll() 不同。expireAll() 会继续显示缓存的数据,同时在后台获取最新数据。

controller.fetchIfStale() (自 0.4 起)​

仅在数据被视为过时时才发起获取;否则返回缓存的数据。 #2804

// Perfect for prefetching - won't overfetch fresh data
const resolveData = async (controller, { owner, repo }) => {
await controller.fetchIfStale(IssueResource.getList, { owner, repo });
};

DevTools 改进​

DevTools 按钮​

开发模式下现在会显示一个浮动按钮,用于快速打开 Redux DevTools 扩展。 #2803

通过 devButton prop 配置或禁用它:

// Disable the button
<CacheProvider devButton={null}>
<App />
</CacheProvider>
// Position it differently
<CacheProvider devButton="top-right">
<App />
</CacheProvider>

持久化的历史记录​

即使页面加载时 DevTools 未打开,也不再丢失历史记录。 2d2e941

更好的状态跟踪​

由于 React 18 会对更新进行批处理,真实状态有时会由多个 action 一起更新。当 devtools 打开时,一个影子状态会准确反映每个 action 带来的变化,便于调试。 c9ca31f

Endpoint 属性可见​

Endpoint 的属性现在可以在 devtool 检查器中完整查看。 a7da00e

Collection 增强​

FormData 参数筛选 (自 0.4 起)​

Collection 现在可以基于 FormData 参数进行筛选。f95dbc6

ctrl.fetch(getPosts.push, { group: 'react' }, new FormData(e.currentTarget));

如果 FormData 中包含 author 字段,新创建的条目会被正确地添加到按该作者筛选的 collection 中。

paginationField (自 0.7 起)​

使用 paginationField 轻松支持分页。 c8c557

const TodoResource = createResource({
path: '/todos/:id',
schema: Todo,
paginationField: 'page',
});

// Fetches page 2 and appends to the collection
ctrl.fetch(TodoResource.getList.getPage, { page: '2' });

其他改进​

  • 为 ErrorBoundary 添加 className,为 AsyncBoundary 添加 errorClassName #2785
  • 新增 getDefaultManagers() 导出,用于显式控制 manager #2791
  • 用 UniversalSuspense + BackupLoading 取代 BackupBoundary #2803
  • Entity.process() 的第四个参数接收 endpoint 的 args (自 0.7 起) a8936f5
  • Collection 新增 nonFilterArgumentKeys,用于在筛选时排除 sort/order 参数 (自 0.7 起) 318df89

迁移指南​

本次升级需要同时更新所有包的版本。

npm install --save @data-client/react@^0.9.0 @data-client/rest@^0.9.0 @data-client/test@^0.9.0 @data-client/img@^0.9.0 @data-client/hooks@^0.9.0

移除 /next 导出​

所有 /next 子路径导出都已移除。之前位于 /next 中的功能现在是标准导出。f65cf83

Before
import { useController } from '@data-client/react/next';
After
import { useController } from '@data-client/react';

移除 makeCacheProvider​

请直接把 provider 组件传给 makeRenderDataClient。 #2787

Before
import { makeCacheProvider } from '@data-client/react';
const renderDataClient = makeRenderDataClient(makeCacheProvider);
After
import { CacheProvider } from '@data-client/react';
const renderDataClient = makeRenderDataClient(CacheProvider);

DELETE -> INVALIDATE action 类型​

为了更清晰,DELETE action 类型已重命名为 INVALIDATE。 #2784

Before
import { DELETE_TYPE } from '@data-client/react';
After
import { INVALIDATE_TYPE } from '@data-client/react';

不再支持遗留 schema​

对遗留 schema 的所有支持都已移除。请确保你使用的是当前的 Entity 类。#2784

  • 移除了 entity.expiresAt
  • 移除了所有为向后兼容而保留的 Entity 覆盖

Schema 序列化器必须支持函数调用​

Schema 序列化器现在必须支持函数调用。Date 不再能直接使用。#2795

Before
class MyEntity extends Entity {
createdAt = new Date();

static schema = {
createdAt: Date,
};
}
After
class MyEntity extends Entity {
createdAt = new Date();

static schema = {
createdAt: (iso: string) => new Date(iso),
};
}

或者,使用 Temporal:

import { Temporal } from 'temporal-polyfill';

class MyEntity extends Entity {
createdAt = Temporal.Instant.fromEpochSeconds(0);

static schema = {
createdAt: Temporal.Instant.from,
};
}

Action 类型加上 'rdc' 前缀​

为了更好的命名空间隔离,所有 action 类型现在都带有 'rdc' 前缀。 #2781

receive -> set action 名称​

所有 'receive' action 名称都已重命名为 'set'。 #2782

Before
import { ReceiveAction, RECEIVE_TYPE } from '@data-client/react';
controller.receive(endpoint, args, response);
After
import { SetAction, SET_TYPE } from '@data-client/react';
controller.set(endpoint, args, response);

简化的 Middleware API​

Middleware 不再以解构属性的形式接收 controller,而是直接接收 controller。#2786

Before
class LoggingManager implements Manager {
getMiddleware = (): Middleware => ({ controller }) => next => async action => {
console.log('before', action, controller.getState());
await next(action);
console.log('after', action, controller.getState());
};

cleanup() {}
}
After
class LoggingManager implements Manager {
getMiddleware = (): Middleware => controller => next => async action => {
console.log('before', action, controller.getState());
await next(action);
console.log('after', action, controller.getState());
};

cleanup() {}
}

getDefaultManagers()​

CacheProvider 元素不再共享默认的 manager。请使用 getDefaultManagers() 进行显式控制。 #2791

import { getDefaultManagers, CacheProvider } from '@data-client/react';

const managers = getDefaultManagers();

<CacheProvider managers={managers}>
<App />
</CacheProvider>

升级支持​

一如往常,如果你遇到任何问题或有疑问,欢迎加入我们的 Chat 或提交 bug