为什么我不在 React 中使用状态管理库

为什么我不再使用 React 状态管理库

https://medium.com/@ipla/why-i-no-longer-use-a-react-state-management-library-7bdffae54600

指数

  • 什么是全局状态管理库?
  • Redux 工具包如何解决全局状态
  • 全球国家的两面性
  • 精益的全局状态方法对于服务器状态使用 TanStack 查询对于共享状态使用 Observables
  • 为什么不使用 React Context?
  • 结论
  • 几年前,当我刚开始学习 React 时,要想成为一名 React 开发人员,你需要了解**两件事**:**TypeScript 和 Redux**。

    如今,情况已经改变。如果您刚刚学习 React 并遇到状态管理的概念,您会对众多的库感到不知所措:Redux、Redux Toolkit、MobX、Jotai、Zustand,等等。

    但在深入探讨之前,让我们先回答一个基本问题:

    什么是全局状态管理库?

    即使你对 React 还比较陌生,你可能也知道术语 **state** 的含义。您使用 `useState` 钩子创建一个,当您更新它时,您的应用程序会重新渲染以反映更改。

    然而,当你需要在 React 应用程序的多个不相连的部分之间共享一些信息时,事情就会变得复杂。

    这就是我们常说的**全局状态**,也是状态管理库试图解决的问题:**如何让任何组件都能访问共享状态**。

    让我们看看 Redux Toolkit 如何处理这个问题。

    Redux 工具包如何解决全局状态

    首先,创建一个**商店**:

    import { configureStore } from '@reduxjs/toolkit';
    
    export const store = configureStore({
      reducer: {},
    });
    
    // Inferindo os tipos de RootState e AppDispatch usando a store
    export type RootState = ReturnType;
    export type AppDispatch = typeof store.dispatch;

    这是您的全局状态存在的**中心位置**。然后使用 **provider** 包装您的应用程序,使所有组件都可以访问它:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'react-redux';
    import App from './App';
    import { store } from './app/store';
    
    ReactDOM.render(
      
        
      ,
      document.getElementById('root')
    );

    现在,您需要定义**切片**,它们是全局状态的各个部分。例如:

    import { createSlice } from '@reduxjs/toolkit';
    
    interface CounterState {
      value: number;
    }
    const initialState: CounterState = { value: 0 };
    export const counterSlice = createSlice({
      name: 'counter',
      initialState,
      reducers: {
        increment: (state) => { state.value += 1; },
        decrement: (state) => { state.value -= 1; },
        incrementByAmount: (state, action) => {
          state.value += action.payload;
        },
      },
    });
    export const { increment, decrement, incrementByAmount } = counterSlice.actions;
    export default counterSlice.reducer;

    最后,连接这个:

    import { configureStore } from '@reduxjs/toolkit';
    import counterReducer from './features/counter/counterSlice';
    
    export const store = configureStore({
      reducer: { counter: counterReducer },
    });

    此时,您可以在组件中使用全局状态,如下所示:

    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import { increment, decrement } from './counterSlice';
    
    export function Counter() {
      const count = useSelector((state) => state.counter.value);
      const dispatch = useDispatch();
      return (
        
    {count}
    ); }

    虽然 Redux Toolkit 大大简化了 Redux,但它仍然包含**大量针对通常简单需求的样板代码**。我们甚至还没有讨论像服务器请求这样的异步操作!

    此外,许多初学者都陷入了将所有状态都放在 Redux 中的陷阱,导致全局存储臃肿且更难管理。

    全球国家的两面性

    仔细想想,大多数“全球状态”可以分为两类:

  • 服务器状态:从服务器获取的数据,例如 CRM 应用程序中的客户列表。
  • 共享 UI 状态:多个地方需要的小块数据,例如当前登录的用户。
  • 让我们分别讨论一下每一个问题。

    精益的全球状态方法

    使用现代工具,您无需专门的状态管理库即可处理全局状态:

    **1.要获取服务器状态,请使用 TanStack 查询**

    TanStack Query(以前称为 React Query)专门用于管理服务器状态。它可以处理缓存、重新加载、陈旧数据等,所有这些都是开箱即用的,您绝对应该使用它,因为您真的不想从头开始重新实现所有这些。例如,搜索客户列表非常简单:

    import { useQuery } from '@tanstack/react-query';
    
    function Customers() {
      const { data, error, isLoading } = useQuery(['customers'], fetchCustomers);
      if (isLoading) return 
    Loading...
    ; if (error) return
    Error: {error.message}
    ; return
      {data.map((customer) =>
    • {customer.name}
    • )}
    ; }

    TanStack Query 消除了手动管理服务器状态的需要,并且由于您获取的状态已被缓存,您可以在所有组件中以完全相同的方式使用它,并且只会被获取一次。

    **2.对于共享状态使用 Observables**

    对于较小的、特定于应用程序的状态(例如用户登录),我使用 **Observable** 模式的轻量级实现:

    export class Observable {
      private _value: T;
      private subscribers = new Set<(value: T) => void>();
    
      constructor(initialValue: T) {
          this._value = initialValue;
        }
        get value() {
          return this._value;
        }
        set value(newValue: T) {
          this._value = newValue;
          this.notify();
        }
        subscribe(callback: (value: T) => void) {
          this.subscribers.add(callback);
          callback(this._value);
          return { unsubscribe: () => this.subscribers.delete(callback) };
        }
        private notify() {
          this.subscribers.forEach((callback) => callback(this._value));
        }
      }
    }

    通过这个,你可以使用 `useSyncExternalStore` 将 Observables 连接到 React:

    import { useSyncExternalStore } from 'react';
    
    export function useObservable(observable: Observable) {
      return useSyncExternalStore(
        (callback) => observable.subscribe(callback).unsubscribe,
        () => observable.value
      );
    }

    现在,您可以像这样创建和使用**可观察对象**:

    const loggedUser = new Observable(user);
    
    loggedUser.value // Para acessar o valor dentro do Observable fora do React
    
    loggedUser.value = someOtherUser // Para definir o valor, tanto fora quanto dentro do React

    在 React 组件内部:

    const user = useObservable(loggedUser);

    为什么不使用 React Context?

    虽然 React Context 在某些情况下有效,但它也有缺点:

  • 范围有限:您无法访问 React 组件之外的 Context。
  • 性能问题:上下文会触发其下方所有组件的重新渲染,即使对于不使用更新值的组件也是如此。
  • 使用**Observables**我们可以避免这两个问题!

    结束

    全局状态管理并不一定很复杂。通过结合 TanStack Query 和轻量级 Observable 模式,您可以简化应用程序,同时避免传统状态管理库的缺陷。