• ReactRedux、Recoil、useContext 做个简单的对比


    关注公众号: 微信搜索 前端工具人 ; 收货更多的干货

    文章来源: 自己掘金文章 https://juejin.cn/post/7073035305430810637/

    React 生态丰富衍生了许多 状态管理 轮子;

    开发时如何选择状态管理,也有些犯难;

    在这把我所理解的、项目中用到过的(React-Redux, Recoil, useContext)做个简单的对比,仅供参考;

    一、个人建议

    React-Redux

    • 可以熟悉 React-Redux 的架构组件思想, 我觉得对日常开发很有用, Redux 重度患者的首选
    • 共享状态多且庞大个人觉得可以优先考虑,结合 combineReducers

    Recoil

    • 日常开发完全够用,使用简单,容易上手,常用 API 也不是很多..., 挺香的
    • 状态渲染导致组件重新渲染这块, Recoil 有做优化, 性能还是很 Ok 的;

    useContext

    • 函数组件开发作为局部共享状态我觉得首选吧; 对于当前页面的共享状态,也交给全局状态管理,这肯定不符合

    自己日常开发

    • 全局状态管理 React-Redux 或者 Recoil(优先选择)
    • 局部状态 useContext, 二者相结合;

    当然还有许多优秀的状态管理插件, MobXhox 等等,自己没怎么用就不多做讨论了。

    以下介绍有不对的,或者有更好的方法,请留言,先谢谢大佬

    因为并不是一直在用 react , 日常开发用的比较杂(vue3,react、flutter、electron都用)、所以见解不是很深,只是把日常开发贴出来,仅供参考

    二、React-Redux

    个人觉得目前来说 React-Redux 相对于其他轮子,优势没那么明显了;

    由于自己是从 Redux 过来的,前期轮子并不是很多选择,所以旧项目一直使用着 React-Redux, 重构时也没替换;

    缺点

    • 语法不够简洁,特别是没出 hook Api 的时候;
    • 组件间的状态共享只能通过将 state 提升至它们的公共祖先来实现,但这样做可能导致重新渲染一颗巨大的组件树

    优点

    React-Redux 架构组件思想我觉得非常的好, 很适用开发时对组件的一个拆分的粒度;

    React-Redux 将所有组件分成两大类

    • UI 组件(presentational component
    1. 只负责 UI 的呈现,不带有任何业务逻辑
    2. 没有状态(即不使用state这个变量)
    3. 所有数据都由参数(props)提供
    4. 不使用任何 ReduxAPI
    5. 又称纯组件,即纯函数一样,纯粹由参数决定它的值, 没有任何副作用
    • 容器组件(container component
    1. 负责管理数据和业务逻辑,不负责 UI 的呈现
    2. 带有内部状态, 可使用 ReduxAPI
    3. 包含输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
    4. 输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去

    UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑

    React-Redux 规定所有的 UI 组件都由用户提供,容器组件则由 React-Redux自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它

    如果一个组件既有 UI又有业务逻辑,那么将它拆分成:外面是一个容器组件,里面包了一个UI 组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图

    代码片段

    // store/index.js
    // 以下类型涉及其他地方引入,就改为 any 了
    import { createStore } from 'redux';
    
    export const Types = {
      CHANGE_DEVELOP: 'CHANGE_DEVELOP',
      CHANGE_LANGUAGE: 'CHANGE_LANGUAGE'
    }
    const onChangeDevelop = <T = string>(val: T) => {
      return {
        type: Types.CHANGE_DEVELOP,
        payload: val
      }
    }
    const onChangeLanguage =  <T = string>(val: T) => {
      return {
        type: Types.CHANGE_LANGUAGE,
        payload: val
      }
    }
    const baseState = {
        language: 'zh',
        developer: '前端工具人'
    }
    interface IAction {
      type: string,
      payload: any
    }
    const reducer = (state = baseState, action: IAction) => {
        switch (action.type) {
            case Types.CHANGE_LANGUAGE:
              return { ...state, language: action.payload};
            case Types.CHANGE_DEVELOP:
              return { ...state, developer: action.payload };
            default:
              return state;
        }
    }
    const configureStore = () => createStore(reducer)
    export default configureStore;
    
    // 项目入口 App.tsx
    import { Provider } from 'react-redux'
    import configureStore from './store/index'
    const store = configureStore()
    ReactDOM.render(
      <React.StrictMode>
        <Provider store={store}>
         <App />
        </Provider>
      </React.StrictMode>,
      document.getElementById('root')
    );
    
    // 使用 Home.tsx
    import {useDispatch, useSelector} from 'react-redux'
    const Header = () => {
        const dispatch = useDispatch()
        const developer = useSelector((state) => state.developer)
        const onChangeDeveloper = ()=> { dispatch({ type:  Types.CHANGE_DEVELOP, developer: '修改后的developer' }) }
    }
    

    三、Recoil

    优势

    我引用的是 Recoil 文档 的介绍, 吧啦吧啦一堆 详情直戳

    • React 官方提供的提供的状态管理库, 这官方2字就很 nice, 大胆的用吧;

    • 定义了一个有向图 (directed graph),正交同时又天然连结于你的 React 树上。状态的变化从该图的顶点(我们称之为 atom)开始,流经纯函数 (我们称之为 selector) 再传入组件

    • 使用 Recoil 会为你创建一个数据流向图,从 atom(共享状态)到 selector(纯函数),再流向 React 组件。Atom 是组件可以订阅的 state 单位。selector 可以同步或异步改变此 state

    • 我们可以定义无需模板代码的 API,共享的状态拥有与 React 本地 state 一样简单的 get/set 接口 (当然如果需要,也可以使用 reducer 等进行封装)。

    • 我们有了与 Concurrent 模式及其他 React 新特性兼容的可能性。

    • 状态的定义是渐进式和分布式的,这使代码分割成为可能。

    • 无需修改对应的组件,就能将它们本地的 state 用派生数据替换。

    • 无需修改对应的组件,就能将派生数据在同步与异步间切换。

    • 我们能将导航视为头等概念,甚至可以将状态的转变编码进链接中。

    • 可以很轻松地以可回溯的方式持久化整个应用的状态,持久化的状态不会因为应用的改变而丢失

    代码片段

    // store/index.js
    // 以下类型涉及其他地方引入,就改为 any 了
    import React from 'react';
    import { atom, selector, } from 'recoil';
    
    // Recoil key 集合
    const KEYS = {
        COMMON_STATE: 'commonState'
        PRODUCT_COUNT: 'productCount'
    }
    interface IBaseStore {
      language: string,
      developer: string,
      email: string,
      children: React.ReactNode[] | React.ReactNode
    }
    export const commonState = atom<IBaseStore>({
      key: KEYS.COMMON_STATE,
      default: {
        language: 'zh',
        developer: 'laisheng',
        children: [],
        email: '18826262167@163.com'
      }
    })
    export const productCount = selector({
      key:  KEYS.PRODUCT_COUNT,
      get: ({get}) => {
        return get(commonState)
      },
      set:({set, reset, get}, newValue) => {
       console.log(newValue)
     }
    })
    
    // App.tsx
    import { RecoilRoot } from 'recoil';
    const App: React.FC<AppProps> = () => {
      return (
        <RecoilRoot>
          <Router>
            <RouterPage />
          </Router>
        </RecoilRoot>
      )
    }
    export default App
    
    // 使用 Home.tsx
    import {useRecoilState, useRecoilValue} from 'recoil'
    import { commonState } from '@/store'
    const Header = () => {
        const [comState, setComState] = useRecoilState(commonState);
        setComState({ ...comState, developer: '更新工具人' })
    }
    
    

    四、useContext

    这个作为 React-Hooks 自带的 buff 还是非常的ok

    这个就没啥好介绍的了, 直接上代码片段吧

    // Home.tsx
    // 以下类型涉及其他地方引入,就改为 any 了
    import React, { createContext, useMemo, useContext, useEffect, useReducer, useRef, useState, memo, useCallback } from 'react';
    
    interface IHomeContext<T> {
      state: T,
      dispatch: any
    }
    interface IState {
      communityId: string | number,
      communityName: string,
      macAddress: string | number,
      doorControlName: string,
    }
    const homeState = {
      communityId: '',
      communityName: '',
      macAddress: '',
      doorControlName: ''
    }
    const HomeContext = createContext<IHomeContext<IState>>({
      state: homeState,
      dispatch: () => [],
    })
    
    // 类型这块这里就直接贴any了,因为要从其他地方导入类型
    const onChangeHomeState = (state: any, data: any): IState => {
      for (let key in data) {
        // 避免 useContext 存储未定义的字段
        if (key in state) state[key] = data[key]
      }
      return { ...state }
    }
    
    const Home: React.FC = () => {
      const [state, dispatch] = useReducer(onChangeHomeState, homeState)
      return (
        <HomeContext.Provider value={{ state: useMemo(() => state, [state]), dispatch }}>
          <div className="home-container">
            <main>
              <Header />
              ...
            </main>
          </div>
        </HomeContext.Provider>
      )
    }
    
    // 使用 
    const Header = () => {
        const { state, dispatch }  = useContext(HomeContext)
        // age、 address onChangeHomeState 并不会处理, 更新的只有 communityId
        dispatch({ 'communityId': 13, age: 18, address: '深圳' })
    }
    
  • 相关阅读:
    Web测试与App测试的区别-总结篇
    Shell之基本用法
    Samba服务部署
    Linux基础(3)
    linux基础(2)
    linux基础(2)
    Linux基础(1)
    网络基础及网络协议
    操作系统简介
    计算机基础重要性
  • 原文地址:https://www.cnblogs.com/ljx20180807/p/15986358.html
Copyright © 2020-2023  润新知