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 实战教学


源码归档

目录
相关文章
|
30天前
|
JavaScript 网络架构
Vue中实现分布式动态路由:基础步骤详解
Vue中实现分布式动态路由:基础步骤详解
19 2
|
30天前
|
存储 移动开发 JavaScript
vuex的工作流程,模块化使用案例分享,及状态持久化
vuex的工作流程,模块化使用案例分享,及状态持久化
24 0
|
30天前
qiankun框架中基于actions机制实现主应用与子应用间的双向通信
qiankun框架中基于actions机制实现主应用与子应用间的双向通信
111 0
|
3月前
|
前端开发 JavaScript API
掌握React表单管理的高级技巧:探索Hooks和Context API如何协同工作以简化状态管理与组件通信
【8月更文挑战第31天】在React中管理复杂的表单状态曾是一大挑战,传统上我们可能会依赖如Redux等状态管理库。然而,React Hooks和Context API的引入提供了一种更简洁高效的解决方案。本文将详细介绍如何利用Hooks和Context API来优化React应用中的表单状态管理,通过自定义Hook `useForm` 和 `FormContext` 实现状态的轻松共享与更新,使代码更清晰且易于维护,为开发者带来更高效的开发体验。
44 0
|
4月前
|
开发框架 JavaScript 前端开发
基于Vue的工作流项目模块中,使用动态组件的方式统一呈现不同表单数据的处理方式
基于Vue的工作流项目模块中,使用动态组件的方式统一呈现不同表单数据的处理方式
|
5月前
|
存储 前端开发 JavaScript
在React中有效地管理组件之间的通信和数据流
在React中有效地管理组件之间的通信和数据流
|
4月前
|
JavaScript 前端开发
前端 JS 经典:单向数据流
前端 JS 经典:单向数据流
31 0
|
6月前
|
JavaScript 前端开发 算法
Vue.js的单向数据流:让你的应用更清晰、更可控
Vue.js的单向数据流:让你的应用更清晰、更可控
|
6月前
|
JavaScript 前端开发 API
【Vue3】Hooks:一种全新的组件逻辑组织方式
【Vue3】Hooks:一种全新的组件逻辑组织方式
|
存储 JavaScript 前端开发
Redux 状态管理库的原理及使用方式
Redux 是一种流行的状态管理库,用于在 JavaScript 应用程序中管理应用的状态。它遵循单一状态树的原则,将整个应用的状态保存在一个状态树中,并通过纯函数来修改状态。Redux 的原理和使用方式如下:
133 0