Recoil JS - 摆脱重新渲染我的应用程序的悬念

Posted

技术标签:

【中文标题】Recoil JS - 摆脱重新渲染我的应用程序的悬念【英文标题】:Recoil JS - getting rid of the suspense re-rendering my app 【发布时间】:2020-10-17 10:24:58 【问题描述】:

我正在我目前正在构建的 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 为我做的方式处理加载状态)。

有没有办法阻止我的应用重新渲染?我已经看到使用recoil Loadables 以避免React.Suspense 的示例,但是当可加载处于loading 状态时,我正在努力获取旧的应用程序状态。


更新

随着我的进一步实验,我能够使用调度方法使其模仿减速器方法:

    我摆脱了动作原子,并将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 似乎是一个反冲的东西,我想我没有做正确的事情来打破它。

【问题讨论】:

【参考方案1】:

警告弹出的原因在here讨论。这基本上是 Recoil 内部的逻辑流程错误,将在下一个版本中修复。 目前您对此无能为力,但它只会在开发模式下弹出,而在生产模式下会忽略警告。

【讨论】:

以上是关于Recoil JS - 摆脱重新渲染我的应用程序的悬念的主要内容,如果未能解决你的问题,请参考以下文章

如何更新 Recoil.js 外部组件中的原子(状态)? (反应)

变异后如何强制App重新渲染?

为啥我没有进入 React 无限重新渲染循环?

Vercel Next.js 在预渲染页面时生成错误

React.js useState hook 导致过多的重新渲染并且无法更新我的状态

React(性能):如何防止每次状态更改时出现不必要的渲染? [复制]