V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nanxiaobei
V2EX  ›  React

flooks v4,自动优化,按需更新。最简单 React Hooks 状态管理器

  •  
  •   nanxiaobei ·
    nanxiaobei · 2021-07-28 12:54:13 +08:00 · 1181 次点击
    这是一个创建于 377 天前的主题,其中的信息可能已经有所发展或是发生改变。

    状态管理的难题

    在 React Hooks 状态管理方案中,通常会遇到联动更新的问题:

    同一模块更新,消费了此模块的组件都会 re-render,即使被更新的 state 在组件中未用到。

    例如 model = { a: 1, b: 2, fn: () => {} },调用 setModel({ a: 2 }),只使用 b 的组件也会 re-render 。而只使用 fn 函数的组件,理论上永远不应被 setModel() 触发更新,但也会 re-render 。

    若不想被关联更新,就得将数据拆的足够细 —— 例如 React.useState() jotai 等,便是这种 "元数据" 思路。而同时还存在另一种 "模块化" 思路,例如 React.useReducer(),希望将同一模块的数据放在一起。

    使用 "元数据",会背离将数据放在同一 model 的初衷,使用 "模块化",又面临着组件联动更新的问题。

    使用 Redux 的传统 class 组件项目中,react-redux 实现了对组件联动更新的性能优化。但纯粹的 React Hooks 项目,只使用 Hooks 实现的状态管理器,却没有一个好的解决方案。

    flooks v4 介绍

    flooks 是一个专用于 React Hooks 的状态管理器,API 极其简单。即支持模块化,又支持模块间的互通,可能是最简单的一个。

    https://github.com/nanxiaobei/flooks

    之前 flooks v3 的 API 是这样的:

    const counter = (now) => ({
      count: 0,
      add() {
        const { count } = now();
        now({ count: count + 1 });
      },
    });
    
    function Counter() {
      const { count, add } = useModel(counter, ['count']); // 借助 deps 知晓注入的 state
      return <div />;
    }
    

    flooks v4 做了一些更新:

    const counter = ({ get, set }) => ({
      count: 0,
      add() {
        const { count } = get(); // get
        set({ count: count + 1 }); // set
      },
    });
    
    function Counter() {
      const { count, add } = useModel(counter); // 再不需要 deps
      return <div />;
    }
    

    主要改变是:

    • now 分为 getset,以让代码更清晰
    • 无需再传入 deps 进行 re-render 控制
    • set 支持函数形式 set(state => ({ count: state.count + 1 })

    除了 API 更新,更重要的是,flooks v4 将背后的更新核心逻辑进行了全部重写,彻底解决了 "模块化" 方案联动更新的问题,从而实现了对更新颗粒度的精细控制。

    Re-render 自动优化方案

    借助 proxy,flooks 实现了惊人的自动优化,完全按需 re-render,React 真正变为 "react"。

    useModel(someModel) 返回一个 proxy,只有真正用到的数据,才会注入组件,若未用到,则不会注入。

    因此无需再添加 deps,因为 useModel() 现在已经可以知道,哪些数据会被解构出来使用,从而只更新用到的数据。

    只使用函数绝不触发 re-render

    const { fn1, fn2 } = useModel(someModel); // A 组件
    
    const { b, setB } = useModel(someModel); // B 组件
    setB(); // A 无 re-render
    

    若 A 中只使用函数,则其它组件更新不触发 A re-render 。

    未使用的 state 绝不触发 re-render

    const { a } = useModel(someModel); // A 组件
    
    const { b, setB } = useModel(someModel); // B 组件
    setB(); // A 无 re-render
    

    若 A 中未使用某些 state,则其它组件更新不触发 A re-render 。

    未使用的 loading 绝不触发 re-render

    const { someFn } = useModel(someModel); // A 组件
    someFn(); // 无 someFn.loading,无额外 re-render
    

    若 A 中未使用 someFn.loadingsomeFn() 不触发额外 re-render 。

    someFn 为异步,普通 loading 方案中,即使 somefn.loading 未用到,re-render 也会触发至少两次(先 true 然后 false)。但使用 flooks,若 somefn.loading 未用到,则完全不会存在隐形 loading 更新。

    真正的 "模块化"

    以上,flooks v4 真正实现了 React Hooks 状态管理方案的 "模块化"。

    即可以将同一模块的数据放在一处,方便统一管理,又不用担心局部更新触发联动更新。更新完全是按需进行,只有用到的数据,才触发更新,未用到的数据,即使是 someFn.loading,也不会注入组件。

    在代码中写出来的,就是会更新的,没有写的,就根本不会存在,再不用担心更新问题。

    一切都是如此符合直觉,再无其它烦人概念。

    结束啦

    flooks v4,喜欢的话,欢迎 ✰

    https://github.com/nanxiaobei/flooks

    6 条回复    2021-07-30 11:53:00 +08:00
    qfdk
        1
    qfdk  
       2021-07-28 13:01:48 +08:00 via iPhone
    感觉好像很不错 等假期回来
    powerfulyang
        2
    powerfulyang  
       2021-07-28 14:51:09 +08:00
    @umijs/plugin-model 这两个看起来好像
    nanxiaobei
        3
    nanxiaobei  
    OP
       2021-07-28 15:29:15 +08:00
    @qfdk #1 😂
    nanxiaobei
        4
    nanxiaobei  
    OP
       2021-07-28 15:29:46 +08:00
    @powerfulyang #2 完全不一样,只是名字里都带 "model","model" 这个名词是 rematch 发扬光大的
    statumer
        5
    statumer  
       2021-07-30 02:13:15 +08:00 via Android
    感觉楼主的这个 loading 功能不错
    nanxiaobei
        6
    nanxiaobei  
    OP
       2021-07-30 11:53:00 +08:00
    @statumer #5 😊
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2944 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:49 · PVG 19:49 · LAX 04:49 · JFK 07:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.