跳到主要内容

v0.13:NextJS App Router、Expo、React 19、Redux

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

平台:

测试:

以下包已并入 @data-client/react:

  • Redux
  • Hooks

其他改进

破坏性变更:

备注

由于发布事故,0.12 版本被跳过了。

平台​

NextJS App Router​

NextJS 12 引入了在 '/app' 目录中进行路由的新方式。它带来了进一步的性能提升,以及动态路由和嵌套路由。这使 NextJS 成为构建富动态应用的可行平台,而不仅仅是静态网站。

其他获取方式需要缓慢的客户端获取;而 Reactive Data Client 则兼具 SPA 和 MPA 两者的优点。#3074、#3093

与 MPA 一样,使用 Data Client 进行 SSR 意味着最快的首次页面加载。与 SPA 一样,Data Client 提供比 MPA 更快的导航、客户端变更以及远程驱动的变更(例如 websocket)。

此外,搭配 SSR 的 Data Client 相比其他客户端获取库依然保持性能优势。

与客户端获取的性能对比
Bar lengths use a log scale
LCP3xClient fetch 1200 ms → Data Client 400 ms
Mutation603xClient fetch 1810 ms → Data Client 3 ms
List to detail navigation6.7xClient fetch 310 ms → Data Client 46 ms

Data Client 对 HTML 进行流式 SSR,优化了应用的初始加载体验。独特之处在于,Data Client 随后立即可交互,且客户端获取次数为 0。这是通过在 SSR 阶段用规范化数据初始化客户端 Store 来实现的。

用法​

在你的根布局中渲染来自 /nextjs 入口的 DataProvider。

app/layout.tsx
import { DataProvider } from '@data-client/react/nextjs';
import { AsyncBoundary } from '@data-client/react';

export default function RootLayout({ children }) {
return (
<html>
<body>
<DataProvider>
<header>Title</header>
<AsyncBoundary>{children}</AsyncBoundary>
<footer></footer>
</DataProvider>
</body>
</html>
);
}

变更演示​

更多演示

实时数据演示​

其他 SSR​

新增 /ssr 入口——完全不再需要 @data-client/ssr 包 d1b9e96

Before
import {
createPersistedStore,
createServerDataComponent,
} from '@data-client/ssr';
After
import {
createPersistedStore,
createServerDataComponent,
} from '@data-client/react/ssr';

这使得将 Data Client 轻松集成到任何 SSR 框架成为可能。我们欢迎为集成任何开源框架所做的贡献。

Expo​

我们已开始着手 Expo/ExpoGo 演示,并借此发现并修复了这些集成中的一些兼容性问题。如果有我们遗漏的问题,请报告给我们。

React 19​

我们现在支持 React 19。#3071

这需要放弃对低于 16.14 版本的支持,因为我们必须使用 React 自身导出的 jsx 运行时。这还带来了减小包体积的额外好处。

Redux​

新增 @data-client/react/redux #3099。这样就不再需要单独的 @data-client/redux 包。

Before
import {
ExternalDataProvider,
PromiseifyMiddleware,
applyManager,
initialState,
createReducer,
prepareStore,
} from '@data-client/redux';
After
import {
ExternalDataProvider,
PromiseifyMiddleware,
applyManager,
initialState,
createReducer,
prepareStore,
} from '@data-client/react/redux';

为 prepareStore() 新增 middlewares 参数 #3099

const { store, selector, controller } = prepareStore(
initialState,
managers,
Controller,
otherReducers,
extraMiddlewares,
);

controller.set()​

controller.set() 可以更新任意 Queryable Schema。#3105、#3129

ctrl.set(
Todo,
{ id: '5' },
{ id: '5', title: 'tell me friends how great Data Client is' },
);

当需要使用派生数据时,值可以是函数。这能避免竞态条件。

const id = '2';
ctrl.set(Article, { id }, article => ({ id, votes: article.votes + 1 }));
备注

响应必须包含足以计算 Entity.pk() 的值

这让通过 Manager 操作 store 变得更加直接。例如,我们可以在收到 websocket 消息时直接设置 Ticker 实体:

更多演示

类型改进​

REST 变更​

路径字符串​

RestEndpoint.path 支持 +、* 和 {}。a6b4f4a

const getThing = new RestEndpoint({
path: '/:attr1?{-:attr2}?{-:attr3}?',
});

getThing({ attr1: 'hi' });
getThing({ attr2: 'hi' });
getThing({ attr3: 'hi' });
getThing({ attr1: 'hi', attr3: 'ho' });
getThing({ attr2: 'hi', attr3: 'ho' });

ctrl.fetch() 与 Union​

ctrl.fetch() 返回值中的 Union 值现在是其 Entity 类的实例。#3063

import { getFeed } from './Feed';

function useFeedHandler() {
  const ctrl = useController();

  const handleSomething = async () => {
    const feed = await ctrl.fetch(getFeed);
    return feed.map(item => {
      if (item.type == 'link') {
        // we can use class defined getter
        return item.anchor;
      }
    });
  }
  return handleSomething;
}

迁移指南​

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

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

actionTypes.SET_TYPE -> actionTypes.SET_RESPONSE_TYPE​

如果你编写了处理 actionTypes.SET_TYPE 的自定义 Manager,需要将其重命名为 actionTypes.SET_RESPONSE_TYPE。

Before
import type { Manager, Middleware } from '@data-client/react';
import { actionTypes } from '@data-client/react';

export default class LoggingManager implements Manager {
middleware: Middleware = controller => next => async action => {
switch (action.type) {
case actionTypes.SET_TYPE:
console.info(
`${action.endpoint.name} ${JSON.stringify(action.response)}`,
);
return next(action);
// actions must be explicitly passed to next middleware
default:
return next(action);
}
};

cleanup() {}
}
After
import type { Manager, Middleware } from '@data-client/react';
import { actionTypes } from '@data-client/react';

export default class LoggingManager implements Manager {
middleware: Middleware = controller => next => async action => {
switch (action.type) {
case actionTypes.SET_RESPONSE_TYPE:
console.info(
`${action.endpoint.name} ${JSON.stringify(action.response)}`,
);
return next(action);
// actions must be explicitly passed to next middleware
default:
return next(action);
}
};

cleanup() {}
}

SetAction -> SetResponseAction​

React 16.14+​

将最低 React 版本从 16.8.4 提高到 16.14。无需修改任何代码。

npm install --save react@16.14 react-dom@16.14

CacheProvider -> DataProvider​

迁移到 DataProvider 以兼容未来版本。它同时也以 CacheProvider 的名称导出,因此这不是破坏性变更。#3095

Before
import { CacheProvider } from '@data-client/react';
import ReactDOM from 'react-dom';

ReactDOM.createRoot(document.body)
.render(
<CacheProvider>
<App />
</CacheProvider>,
);
Before
import { DataProvider } from '@data-client/react';
import ReactDOM from 'react-dom';

ReactDOM.createRoot(document.body)
.render(
<DataProvider>
<App />
</DataProvider>,
);

升级支持​

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