Recoil JS - 摆脱重新渲染我的应用程序的悬念
Recoil JS - getting rid of the suspense re-rendering my app
我正在我正在构建的 React 框架中试验 Recoil。在我的用例中,应用程序将根据用户活动生成一个操作对象,然后将其发送到服务器,从服务器获取应用程序状态。
动作存储在后坐力中atom
。我正在使用接受动作并从服务器获取状态的后坐力 selectorFamily
。以下是我实际在做什么的简单示例(代码在打字稿中):
export const MyActionAtom = atom<MyAction|undefined>({
key: "MyActionAtom",
default: undefined
});
const MyStateSelectorFamily = selectorFamily({
key: 'MyStateSelectorFamily',
get: (action: MyAction|undefined) => async ({get}) => {
if (action) {
return await getStateFromServer(action);
}
return SomeFallbackState;
}
});
const MyStateSelector = selector({
key: 'MyStateSelector ',
get: ({get}) => {
return get(MyStateSelectorFamily(get(MyActionAtom)));
}
});
function App() {
const appState = useRecoilValue(MyStateSelector);
return (
<RecoilRoot>
<React.Suspense fallback={<div>Loading...</div>}>
<MyApp state={appState} />
</React.Suspense>
</RecoilRoot>
);
}
这里,MyApp
是一个 React 组件,它将根据 appState 渲染应用程序的整个视图树。
现在,从 MyApp
的直接或间接子组件中可以触发另一个操作:
const setMyAction = useSetRecoilState(MyActionAtom);
setMyAction(...);
setMyAction
的结果与我初始化应用程序所用的初始状态相比,值略有偏差。因此,我只想重新渲染依赖于更改状态的组件。
但是,我的整个视图树都得到了更新。我怀疑的原因是 React.Suspense
正在用回退替换 dom 直到返回操作的响应(注意 MyStateSelectorFamily
是异步的,因为状态作为承诺返回)。
在一个更像 redux 的世界中,我会在 promise 解决后触发我的请求并发送状态更改(我打算以不同于 React.Suspense
为我做的方式处理加载状态)。
有没有办法阻止我的应用程序重新呈现?我已经看到使用后坐力 Loadables
以避免 React.Suspense
的示例,但是当可加载项处于 loading
状态时,我正在努力获取旧的应用程序状态。
更新
随着我的进一步实验,我能够使用调度方法使其模仿 reducer 方法:
我去掉了动作原子,把MyStateSelectorFamily
改成了原子本身:
const MyStateAtom = atom({
key: 'MyStateAtom ',
default: SomeFallbackState
});
我用自定义挂钩替换了组件中的 setMyAction(...);
部分:
// in some dedicated ts file:
export const useAction = () => {
const dispatch = useSetRecoilState(MyStateAtom);
return useCallback(async (action: MyAction) => {
await getStateFromServer(action).then(dispatch);
}, [dispatch]);
};
我在一个组件中使用了钩子:
export const SomeComponent = (props: SomeProps) => {
const dispatchAction = useAction();
// other logic
const action: MyAction = ... // create the appropriate action
dispatchAction(action);
return (...);
};
好吧,这似乎可以按照我的意愿工作,并且更接近我以前使用 redux 的代码库,所以我非常喜欢这种方法,而不是以前的 selectorFamily
方法。但是,我经常在控制台中看到反应警告:
Warning: Cannot update a component (`Batcher`) while rendering a
different component (`SomeComponent`) ....
我的应用是 SPA 应用,在初始加载时会打印警告。然后它可以正常工作,没有任何可观察到的问题。我想知道我是否可以解决警告的原因——这 Batcher
似乎是后坐力问题,我想我没有做正确的事情来打破它。
讨论弹出警告的原因here。这基本上是 Recoil 内部的逻辑流程错误,将在下一个版本中修复。
目前您无能为力,但它只会在开发模式下弹出,而在生产模式下警告会被忽略。
我正在我正在构建的 React 框架中试验 Recoil。在我的用例中,应用程序将根据用户活动生成一个操作对象,然后将其发送到服务器,从服务器获取应用程序状态。
动作存储在后坐力中atom
。我正在使用接受动作并从服务器获取状态的后坐力 selectorFamily
。以下是我实际在做什么的简单示例(代码在打字稿中):
export const MyActionAtom = atom<MyAction|undefined>({
key: "MyActionAtom",
default: undefined
});
const MyStateSelectorFamily = selectorFamily({
key: 'MyStateSelectorFamily',
get: (action: MyAction|undefined) => async ({get}) => {
if (action) {
return await getStateFromServer(action);
}
return SomeFallbackState;
}
});
const MyStateSelector = selector({
key: 'MyStateSelector ',
get: ({get}) => {
return get(MyStateSelectorFamily(get(MyActionAtom)));
}
});
function App() {
const appState = useRecoilValue(MyStateSelector);
return (
<RecoilRoot>
<React.Suspense fallback={<div>Loading...</div>}>
<MyApp state={appState} />
</React.Suspense>
</RecoilRoot>
);
}
这里,MyApp
是一个 React 组件,它将根据 appState 渲染应用程序的整个视图树。
现在,从 MyApp
的直接或间接子组件中可以触发另一个操作:
const setMyAction = useSetRecoilState(MyActionAtom);
setMyAction(...);
setMyAction
的结果与我初始化应用程序所用的初始状态相比,值略有偏差。因此,我只想重新渲染依赖于更改状态的组件。
但是,我的整个视图树都得到了更新。我怀疑的原因是 React.Suspense
正在用回退替换 dom 直到返回操作的响应(注意 MyStateSelectorFamily
是异步的,因为状态作为承诺返回)。
在一个更像 redux 的世界中,我会在 promise 解决后触发我的请求并发送状态更改(我打算以不同于 React.Suspense
为我做的方式处理加载状态)。
有没有办法阻止我的应用程序重新呈现?我已经看到使用后坐力 Loadables
以避免 React.Suspense
的示例,但是当可加载项处于 loading
状态时,我正在努力获取旧的应用程序状态。
更新
随着我的进一步实验,我能够使用调度方法使其模仿 reducer 方法:
我去掉了动作原子,把
MyStateSelectorFamily
改成了原子本身:const MyStateAtom = atom({ key: 'MyStateAtom ', default: SomeFallbackState });
我用自定义挂钩替换了组件中的
setMyAction(...);
部分:// in some dedicated ts file: export const useAction = () => { const dispatch = useSetRecoilState(MyStateAtom); return useCallback(async (action: MyAction) => { await getStateFromServer(action).then(dispatch); }, [dispatch]); };
我在一个组件中使用了钩子:
export const SomeComponent = (props: SomeProps) => { const dispatchAction = useAction(); // other logic const action: MyAction = ... // create the appropriate action dispatchAction(action); return (...); };
好吧,这似乎可以按照我的意愿工作,并且更接近我以前使用 redux 的代码库,所以我非常喜欢这种方法,而不是以前的 selectorFamily
方法。但是,我经常在控制台中看到反应警告:
Warning: Cannot update a component (`Batcher`) while rendering a different component (`SomeComponent`) ....
我的应用是 SPA 应用,在初始加载时会打印警告。然后它可以正常工作,没有任何可观察到的问题。我想知道我是否可以解决警告的原因——这 Batcher
似乎是后坐力问题,我想我没有做正确的事情来打破它。
讨论弹出警告的原因here。这基本上是 Recoil 内部的逻辑流程错误,将在下一个版本中修复。 目前您无能为力,但它只会在开发模式下弹出,而在生产模式下警告会被忽略。