013 Umi 的数据流最佳实践(2/2) - 全局的 hooks 数据流

简介: 013 Umi 的数据流最佳实践(2/2) - 全局的 hooks 数据流

image.png


在上节课中我们掌握了 React 的页面级数据流,我们知道了,在单个组件内使用 useState ,不同页面建的数据是没办法共享的。 但是在实际业务中我们经常会遇到两个或者多个页面之间需要同时维护一份数据的情况,因此就有了全局的 hooks 数据流方案。


这节课我们会现将原理再讲最佳实践。



组件间数据独立

新建页面文件 src/pages/hooks.tsx

import React, { useState } from "react";
const Count = () => {
  const [count, setCount] = useState(1);
  return <div onClick={() => setCount(count + 1)}>{count}</div>;
};
export default function List() {
  return (
    <div>
      <Count />
      <br />
      <Count />
      <br />
      <Count />
    </div>
  );
}
复制代码

我们编写一个简单的计数器,在页面中使用了三次,运行页面你将看到三个计数器,之间的数据是独立的,相互没有影响的。



让组件间数据同步

简单的修改一下我们的代码,将数值和方法传到组件内部,这时候我们再运行我们的页面,就会看到所有的数值都保持一致了,因为他们共用的是同一个数据。

import React, { useState } from "react";
const Count = ({ count, setCount }) => {
  return <div onClick={() => setCount(count + 1)}>{count}</div>;
};
export default function List() {
  const [count, setCount] = useState(1);
  return (
    <div>
      <Count count={count} setCount={setCount} />
      <br />
      <Count count={count} setCount={setCount} />
      <br />
      <Count count={count} setCount={setCount} />
    </div>
  );
}
复制代码


但是上面的修改,我们需要将这两个属性一直往下传,如果组件层级很深的话,这么写非常的难维护。因此我们可以使用 React 的上下文来管理。


import React, { useState, createContext, useContext } from "react";
const Context = createContext<any>(null);
const Count = ({}) => {
  const { count, setCount } = useContext(Context);
  return <div onClick={() => setCount(count + 1)}>{count}</div>;
};
export default function List() {
  const [count, setCount] = useState(1);
  return (
    <Context.Provider value={{ count, setCount }}>
      <Count />
      <br />
      <Count />
      <br />
      <Count />
    </Context.Provider>
  );
}
复制代码

这样不管我们的组件嵌套多深,它都可以直接取到页面的数据,这个小技巧在开发中可以选择性的使用。



Umi 中的纯 hooks 数据流

上面的例子,我们只是将数据提升到全局维护,但在 Umi 中,它做的更多。 约定存在 src/models 目录下面的文件,只要导出了自定义 hook ,会被识别为 model,添加到全局的 hooks 数据流中。可以在任何 React 上下文中,使用 useModel 取到你需要的数据。 比如,存在文件src/models/user.tsx,内容如下:

import { useState, useCallback } from "react";
export default function useAuthModel() {
  const [user, setUser] = useState("umi");
  const fetchUser = useCallback(() => {
    setUser("umi@4 实战教学");
  }, []);
  return {
    user,
    fetchUser,
  };
}
复制代码


可以直接在页面中使用

import React from "react";
import { useModel } from "umi";
export default () => {
  const { user, fetchUser } = useModel("user", (model) => ({
    user: model.user,
    fetchUser: model.fetchUser,
  }));
  return <div onClick={() => fetchUser()}>hello {user}</div>;
};
复制代码

useModel 有两个参数,useModel(namespace,updater)。

namespace 就是 hooks model 文件的文件名,如上面例子里的 user


updater - 可选参数。在 hooks model 返回多个状态,但使用组件仅引用了其中部分状态,并且希望仅在这几个状态更新时 rerender 时使用(性能相关)。



性能优化

useModel() 方法可以接受可选的第二个参数 updater,当组件只需要使用 Model 中的部分参数,而对其它参数的变化不感兴趣时,可以传入一个函数进行过滤。以实现计数器的操作按钮为例:


// src/components/CounterActions/index.tsx
import { useModel } from 'umi';
export default () => {
  const { add, minus } = useModel('useCounterModel', (model) => ({
    add: model.increment,
    minus: model.decrement,
  }));
  return (
    <div>
      <button onClick={add}>add by 1</button>
      <button onClick={minus}>minus by 1</button>
    </div>
  );
};
复制代码


上面的组件并不关心计数器 Model 中的 counter 值,只需要使用 Model 提供的 increment()decrement() 方法。于是我们传入了一个函数作为 useModel() 方法的第二个参数,该函数的返回值将作为 useModel() 方法的返回值。

这样,我们过滤掉了 counter 这一频繁变化的值,避免了组件重复渲染带来的性能损失。



插件实现

略,插件实现会在我们讲解完 Umi 插件开发之后,会有专门的课程讲解实现细节。



使用 Umi model 插件

pnpm i @umijs/plugins@4.0.0-rc.20
复制代码

注意指定版本号,为了这个课程在不久的将来仍然可用,我们锁定了 Umi 的版本。


增加配置 config/config.ts,使用 model 插件,并且配置 model 来开启插件功能。

import { defineConfig } from "umi";
export default defineConfig({
  plugins: [require.resolve("@umijs/plugins/dist/model")],
  model: {},
});
复制代码


新建文件 src/models/user.tsx 写入上面 user.tsx 的演示代码。 新建文件 src/pages/usemodel.tsx 写入上面页面的演示代码。

运行 pnpm start 你将在页面中看到 hello umi 当你点击它时,它会变成 hello umi@4 实战教学


源码归档

目录
相关文章
|
5天前
|
JavaScript 前端开发 算法
Vue.js的单向数据流:让你的应用更清晰、更可控
Vue.js的单向数据流:让你的应用更清晰、更可控
|
5天前
|
JavaScript 前端开发 API
【Vue3】Hooks:一种全新的组件逻辑组织方式
【Vue3】Hooks:一种全新的组件逻辑组织方式
|
5天前
|
前端开发 JavaScript API
React 生态系统:路由、状态管理、调试、测试、组件库、文档……
React 生态系统:路由、状态管理、调试、测试、组件库、文档……
46 0
|
8月前
|
JavaScript
【Vue】模板语法,事件处理器及综合案例、自定义组件、组件通信
组件(Component)是Vue最强大的功能之一组件可以扩展HTML元素,封装可重用的代码组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树。
|
9月前
|
资源调度 JavaScript 前端开发
Vue 3 状态管理进阶:使用 Pinia 构建可扩展的应用程序
Vue 3 状态管理进阶:使用 Pinia 构建可扩展的应用程序
155 0
|
10月前
|
存储 JavaScript 前端开发
Redux 状态管理库的原理及使用方式
Redux 是一种流行的状态管理库,用于在 JavaScript 应用程序中管理应用的状态。它遵循单一状态树的原则,将整个应用的状态保存在一个状态树中,并通过纯函数来修改状态。Redux 的原理和使用方式如下:
|
编解码 算法 前端开发
以寡治众各个击破,超大文件分片上传之构建基于Vue.js3.0+Ant-desgin+Tornado6纯异步IO高效写入服务
分治算法是一种很古老但很务实的方法。本意即使将一个较大的整体打碎分成小的局部,这样每个小的局部都不足以对抗大的整体。战国时期,秦国破坏合纵的连横即是一种分而治之的手段;十九世纪,比利时殖民者占领卢旺达, 将卢旺达的种族分为胡图族与图西族,以图进行分裂控制,莫不如是。
以寡治众各个击破,超大文件分片上传之构建基于Vue.js3.0+Ant-desgin+Tornado6纯异步IO高效写入服务
|
JavaScript 前端开发 API
Zag-基于状态机的组件库
本文适合对状态机感兴趣的小伙伴阅读
Zag-基于状态机的组件库
|
JavaScript 测试技术 开发工具
|
存储 前端开发 JavaScript
为什么我使用 Umi 的 model 简易数据流管理插件
Umi 是一款企业级的 React 前端应用框架,云巧产业数字组件中心推荐使用基于 Umi 的 Koi 框架统一前端应用研发流程,支撑前端项目从研发、联调到上线、发布的全流程。 本文假设您正在或计划使用 Umi 或 Koi 作为底层框架支撑前端应用的开发,并且对 Umi 有一定的了解。 ## 数据治理的原则 React 的核心特征是“数据驱动视图”,用公式表达即 `UI = render(d