总结本身使用过的Hooks数据流方式

Hooks正式推出也有一年出头了,总结这一年中,本身使用过的Hooks数据流方式react

目前接触过的数据流方案大体是2种方式redux

  • 基于React的数据流
  • 不基于React的数据流

基于React的数据流实现

简单讲来,就是使用官方提供的Hooks,好比最多见的useState,这种方式也是咱们用的最多的api

useState

经过useState能将曾经类组件的state值拆分为多个闭包

function App() {
  const [state, setState] = useState({ foo: 0 });
  const [loading, setLoading] = useState(false);
  // ...
}
复制代码

使用useState最大的好处是简洁明了,我的以为有2个缺点app

  1. 缺点在于拆分过多,使用太多的useState后很差管理
  2. 若是某一个state值过于复杂,在改变值时的合并是比较难处理的

针对这两个小问题,也有另外一个hooks解决框架

useReducer

useReducer能够说是官方简版redux异步

const reducer = (state, { type, loading }) => {
  if (type === "FOO") return { ...state, foo: 1 };
  if (type === "SET_LOADING") return { ...state, loading };
  return state;
};
function App() {
  const [state, dispatch] = useReducer(reducer, { foo: 0, loading: false });
  // ...
}
复制代码

redux还有一些小差别的就是react提倡将初始值赋值在useReudcer的参数中,而不是reducerstateide

上面2种方式在单组件或者是父子层级组件使用仍是比较方便,若是想像redux同样不关乎层级共享数据呢?在Hooks中也提供了对应的方法工具

useContext + useReducer

这种方式是我在这一年中使用最多的跨组件共享状态的方式了学习

const Context = createContext({});
const reducer = (state, { type, loading }) => {
  if (type === "FOO") return { ...state, foo: 1 };
  if (type === "SET_LOADING") return { ...state, loading };
  return state;
};
function App() {
  const [state, dispatch] = useReducer(reducer, { foo: 0, loading: false });
  return (
    <Context.Provider value={{ state, dispatch }}> // ...children </Context.Provider> ); } function Foo() { const { state, dispatch } = useContext(Context); // ... } 复制代码

这种方式能将本身的hooks跨组件共享状态了,使用仍是比较方便,惟一的缺点就是本身须要使用createContext来建立Context而且挂载Provider,会多一点步骤,也须要本身管理Context,这能够说是纯手动

unstated-next

unstated-next是一个200 字节的状态管理解决方案

function useCounter(initialState = 0) {
  const [count, setCount] = useState(initialState)
  return { count, setCount }
}

const Counter = createContainer(useCounter);

function Foo() {
  const counter = Counter.useContainer();
  // ...
}

function App() {
  return (
    <Counter.Provider> <Foo /> </Counter.Provider> ) } 复制代码

unstated-next的实现使用的全是Reactapi,源码也短,下面会对他的源码进行分析

使用unstated-next让咱们不须要本身建立和管理Context了,从纯手动切换到了半自动

UmiJS中提供的useModel

UmiJS中提供了一个useModel方法,能够很方便的将hooks全局使用,它的默认规则是src/models下导出的hooks会做用于全局

// src/models/count.ts
export default () => {
  const [count, setCount] = useState(0);
  return { count, setCount };
};
// 
function App() {
  const { count } = useModel('count');
  // ...
}
复制代码

useModel等因而省去了上面的useContext和挂载<Prvoider>的步骤,由框架处理了,在React Developer Tools能够看到最外层是有一个存了导出hooks的值的Provider的,我想他的实现方式应该和unstated-next相似

从本身建立和管理Context,挂载Provider,到本身挂载Provder,再到只须要写hooks逻辑,过程就是手动——半自动——全自动

它的缺点就是范围局限了,仅限于UmiJS框架

基于React的数据流优缺点

仅在使用过程当中我的的总结

优势

  • 基于React,没有额外的学习过程,简单易用

缺点

  • 没法作到精确刷新
  • 只是简单是数据流管理,并不包含常见的异步数据的处理

Context没法作到精确刷新

数据是一个总体,不能作到精确刷新,一旦改变React就会自动触发刷新

function Foo() {
  const { state: { foo } } = useContext(Context);
  // ...
}
function Bar() {
  const { state: { bar } } = useContext(Context);
  // ...
}
复制代码

若是在<Foo>中调用了dispatch()state.foo进行了更改,<Bar>也会刷新

不基于React的数据流实现

不基于React的数据流实现就是数据存储不在React里,数据改变不会直接触发组件刷新,而是经过其余的方式触发组件从新渲染,我只使用过2种

  • Redux+React-Redux
  • DvaJs

React-Redux

Hooks推出后,React-Redux也更新了Hooks方法,使用useSelector()来取得state

const Counter = () => {
  const counter = useSelector(state => state.counter)
  // ...
}
复制代码

它的优势是会精确刷新,不会像Context同样致使总体刷新,由于useSelector的从新渲染是本身控制的,而不是交给React处理

Redux+React-Redux的缺点我想用过的都知道,就是须要管理不少文件

DvaJS

DvaJS其实没有提供Hooks的数据流方式

DvaJS单独使用不多,基本是使用UmiJS,实际在Umi中有Hooks的方式去获取数据

云谦大佬可能全身心投入UmiJS开发,已经有很长一段时间没更新了,可是我以为做为一个集成度很是高的优秀数据流管理。

仅仅使用过2个月的我对DvaJS的总结

优势

  • 集成度很高,约定式,使用了redux-sage解决异步数据流问题
  • redux+react-redux简单不少,再也不会有文件管理问题
  • 约定式的动态增长reducer,让一些数据能够懒加载

小小的总结

在类组件时代,没法拆分state,类组件感受就很重,组件级state多了也很难管理,Context流行度也不算高,使用<Context.Consumer>让组件更重了,后来有了contextType也没有useContext这么简洁,因此感受以前流行Redux也是有缘由的,由于React自己没有提供好的状态管理

Hooks时代,一切都变得更简洁,官方提供了useReducer这样的简洁版Redux,同时组件级的state也更简单,使用自定义的Hooks,让数据和视图耦合更低了,useContext+useReducer的方案能够解决大部分须要共享状态的场景

使用了挺久的React,我感受不少场景都不会全局共享状态,我如今作的项目就是后台管理系统,页面的数据也不会和别的页面关联,我都使用Context一把梭了,因此我更喜欢轻量级的Hooks数据流解决方案

看看unstated-next源码

有时候感受本身真的变成了搬运工,缺少本身的想法,就很呆,上面提到,我很长一段时间都是用useContext来共享状态,每次都是手动挡,前几天忽然想,为何本身要作重复的工做,一搜果真有大佬已经写好了

// Provider传入的Props
export interface ContainerProviderProps<State = void> {
  initialState?: State;
  children: React.ReactNode;
}
// createContainer建立的Container类型
export interface Container<Value, State = void> {
  Provider: React.ComponentType<ContainerProviderProps<State>>;
  useContainer: () => Value;
}

// 建立一个Container
export function createContainer<Value, State = void>(
  // 自定义数据的hook
  useHook: (initialState?: State) => Value
): Container<Value, State> {
  // Context用来传递数据
  let Context = React.createContext<Value | null>(null);

  function Provider(props: ContainerProviderProps<State>) {
    // 用初始数据初始化自定义的hook
    let value = useHook(props.initialState);
    // 将hook的返回值赋值给Provider
    return <Context.Provider value={value}>{props.children}</Context.Provider>;
  }

  // 使用Container,值就是自定义的hook的返回值
  function useContainer(): Value {
    let value = React.useContext(Context);
    if (value === null) {
      throw new Error("Component must be wrapped with <Container.Provider>");
    }
    return value;
  }

  return { Provider, useContainer };
}

export function useContainer<Value, State = void>(
  container: Container<Value, State>
): Value {
  return container.useContainer();
}
复制代码

源码很简单,就是一个闭包

做者说 “我相信 React 在状态管理方面已经很是出色”、“我但愿社区放弃像 Redux 这样的状态管理库,并找到使用 React 内置工具链的更好方法”,我以为颇有道理

虽然感受这篇没有什么技术含量,可是没有灵感,也有三周没有分享文章了


最后,祝你们身体健康,工做顺利!

欢迎你们关注个人公众号~


参考文章:精读《React Hooks 数据流》

相关文章
相关标签/搜索