为什么需要数据流方案?

React本身只是一个DOM的抽象层,也就是使用组件来构建虚拟DOM,而开发大型应用,还需要解决数据流和组件通信的问题

通信问题

组件常见的通信如下

  • 向子组件发消息
  • 向父组件发消息
  • 向兄弟组件/多层级嵌套的其他组件发消息

    React只提供了一种通信手段:props传参,对于大型应用很不方便

数据流方案的演进

  • props组件传递 /context 上下文

    React提供的最基本的单项数据流/跨层级数据流方案

    • 缺点:大型应用使用不方便
  • redux/mobx

    社区提供的应用级的顶层状态管理

    • 缺点:概念繁多,需要在多个文件中穿插
  • dva

    umi基于redux+redux-saga的数据流方案

    • 缺点:概念繁多,需要借助connect高阶函数实现数据流和组件的绑定
  • react hooks

    纯hooks的数据流方案

    • 缺点**:必须在React的上下文环境中才可以**

      umi的数据流最佳实践方案

      页面级hooks数据流

      独立管理页面自己控制层的状态

  1. import { useState } from 'React'
  2. const Counter = ()=>{
  3. const [ count,setCount] = useSate<number>(0)
  4. return (
  5. <div onClick={()=>{
  6. setCount(count=>count+1)
  7. }}>{count}</div>
  8. )
  9. }
  10. export default Counter

全局hooks的数据流

多个页面需要共享的状态

使用React的上下文管理相互关联的嵌套组件

  • React.createContext
  • useContext ```javascript import React, { useState, createContext, useContext } from “react”;

const Context = createContext(null);

const Counter = ({}) => { const { count, setCount } = useContext(Context);

return

setCount(count + 1)}>{count}
; };

export default function List() { const [count, setCount] = useState(1); return ( ); }

  1. <a name="Y8CSU"></a>
  2. ### umi中的纯Hooks数据流
  3. <a name="bnmtt"></a>
  4. #### 使用umi model插件
  5. > 安装umi的插件
  6. ```javascript
  7. pnpm i @umijs/plugins

开启model插件配置

  1. // config/confit.ts
  2. import { defineConfig } from "umi";
  3. export default defineConfig({
  4. plugins: [require.resolve("@umijs/plugins/dist/model")],
  5. model: {},
  6. });

在 Umi 中约定存在 src/models 目录下面的文件,只要导出了自定义 hook ,会被识别为 model,添加到全局的 hooks 数据流中。可以在任何 React 上下文中,使用 useModel 取到你需要的数据

useModel 有两个参数 [性能优化]

  • namespace 就是 hooks model 文件的文件名,如上面例子里的 counter。
  • updater - 可选参数。在 hooks model 返回多个状态,但使用组件仅引用了其中部分状态,并且希望仅在这几个状态更新时 rerender 时使用(性能相关) ```javascript import { useState } from “react”; import { useModel } from “umi”;

// src/models/useCounterModel.tsx export default function useCounterModel() { const [count, setCount] = useState(0); // increment const increment = ()=>{ setCount(count=>count+1) } //decrement const decrement = ()=>{ setCount(count=>count-1) } return { count, increment, decrement }; }

// src/pages/Counter.tsx const Counter = ({}) => { const { count, setCount } = useModel(“counter”, (model) => ({ count: model.count, add: model.increment, }));

return

setCount(count + 1)}>{count}
; };

// src/pages/index.tsx

export default function List() { const [count, setCount] = useState(1); return ( <> <> ); }

```

组件并不关心计数器 Model 中的 decrease 操作,只需要使用 Model 提供的 increment() 方法。于是我们传入了一个函数作为 useModel() 方法的第二个参数,该函数的返回值将作为 useModel() 方法的返回值。这样,我们过滤掉了 counter 这一频繁变化的值,避免了组件重复渲染带来的性能损失。