面试官:React第三方库管理状态有哪些。我:哪些!?

931 阅读5分钟

当时先说了redux,觉得不够说,再说了内置的useStateuseContextuseReducer等钩子函数。(完了,开头就难倒了) 我们来介绍一下 ReduxzustandRecoil,以及简化版本 Jotai。我会用最简单的语言来讲解,让你快速理解它们的原理和用法。

1. Redux:通过 actionreducer 更新状态

核心概念: Redux 是一个状态管理库,它的核心思想是把整个应用的状态存储在一个单一的、不可变的 state tree(状态树)中。状态的更新需要通过一系列的步骤来完成,主要包括 actionreducer

(1)Action

  • 定义:Action 是一个普通的 JavaScript 对象,用来描述发生了什么。它必须有一个 type 属性,表示动作的类型。

  • 示例

    const addTodoAction = {
        type: "ADD_TODO",
        payload: "Learn Redux"
    };
    

(2)Reducer

  • 定义:Reducer 是一个纯函数,它接收当前状态和一个 action,然后返回新的状态。

  • 规则Reducer 必须是纯函数,不能修改原状态,只能返回新的状态。

  • 示例

    function todoReducer(state = [], action) {
        switch (action.type) {
            case "ADD_TODO":
                return [...state, action.payload];
            default:
                return state;
        }
    }
    

(3)Store

  • 定义:Store 是一个容器,用来保存状态树,并提供方法来更新状态。

  • 如何使用

    import { createStore } from "redux";
    
    const store = createStore(todoReducer);
    
    // 订阅状态变化
    store.subscribe(() => console.log(store.getState()));
    
    // 派发 action 更新状态
    store.dispatch({ type: "ADD_TODO", payload: "Learn Redux" });
    

(4)问题:Redux 太繁琐了!

Redux 的代码量较多,配置复杂,尤其是对于小型项目来说,显得有些“大炮打蚊子”。为了解决这个问题,出现了 Redux Toolkit


2. Redux Toolkit:简化 Redux 的使用

核心工具: Redux Toolkit 是一个官方提供的工具包,用来简化 Redux 的开发。它内置了 createSlicecreateAsyncThunk 等工具。

(1)createSlice

  • 作用:简化 reducer 和 action 的创建过程。

  • 示例

    import { createSlice } from "@reduxjs/toolkit";
    
    const todoSlice = createSlice({
        name: "todo",
        initialState: [],
        reducers: {
            addTodo(state, action) {
                state.push(action.payload);
            },
            removeTodo(state, action) {
                state.splice(action.payload.index, 1);
            }
        }
    });
    
    export const { addTodo, removeTodo } = todoSlice.actions;
    export default todoSlice.reducer;
    

(2)createAsyncThunk

  • 作用:简化异步操作的处理。

  • 示例

    import { createAsyncThunk } from "@reduxjs/toolkit";
    
    // 异步 action
    const fetchTodos = createAsyncThunk("todos/fetchTodos", async () => {
        const response = await fetch("https://jsonplaceholder.typicode.com/todos");
        return response.json();
    });
    

(3)优点**

  • 更简洁:代码量大幅减少。
  • 更易用:内置了对异步操作的支持。
  • 更安全:默认使用了不可变更新。

Zustand

Zustand 是一个基于 Hooks 的状态管理库,专为 React 设计,旨在提供一种简单而强大的状态管理方式。它的核心理念是“最小化”,即只管理应用中真正需要的状态,而不是强迫使用全局状态,从而提高性能和可维护性 (看中zustand的小巧和按需订阅模式)

1. 安装 Zustand
npm install zustand

或者使用 yarn

yarn add zustand
2. 创建 Store

使用 create 函数创建一个 store,store 是一个 JavaScript 对象,包含状态和修改状态的方法。

import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));
3. 在组件中使用 Store

通过 useStore Hook 访问和更新状态。

import React from 'react';
import { useStore } from './store';

const Counter = () => {
  const { count, increment, decrement } = useStore();
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </div>
  );
};

四、高级功能

1. 异步操作

Zustand 支持异步操作,例如从 API 获取数据。

import create from 'zustand';

const useStore = create((set) => ({
  user: null,
  loading: false,
  fetchUser: async (id) => {
    set({ loading: true });
    try {
      const response = await fetch(`https://api.example.com/users/${id}`);
      const user = await response.json();
      set({ user, loading: false });
    } catch (error) {
      set({ loading: false, error });
    }
  },
}));
2. 状态持久化

通过 persist 中间件,可以将状态持久化到 localStoragesessionStorage

import create from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    { name: 'counter-storage' }
  )
);
3. 中间件支持

Zustand 支持多种中间件,例如 devtools 中间件,可以在浏览器开发者工具中调试状态。

import create from 'zustand';
import { devtools } from 'zustand/middleware';

const useStore = create(
  devtools((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
  }))
);

zustand 小结

zustand 通过gzip 压缩后仅1kb,对项目性能几乎没有影响。通过按需订阅模式,减少不必要的组件渲染。在灵活方面,支持异步,使用persist中间件进行持久化,也支持devtool 适用开发调试。但是社区相比redux 小了点,资源有限,高级功能也少了点。

3. Recoil:Facebook 推出的状态管理库

核心概念: Recoil 是 Facebook 推出的一个状态管理库,它基于 原子(atom)选择器(selector) 的概念,支持细粒度的状态管理。

(1)原子(Atom)

  • 定义:原子是状态的基本单位,类似于 Redux 中的 state

  • 示例

    import { atom } from "recoil";
    
    const todoAtom = atom({
        key: "todoAtom",
        default: []
    });
    

(2)选择器(Selector)

  • 定义:选择器是一个函数,可以根据原子的状态计算出新的状态。

  • 示例

    import { selector } from "recoil";
    
    const todoCountSelector = selector({
        key: "todoCountSelector",
        get: ({ get }) => {
            const todos = get(todoAtom);
            return todos.length;
        }
    });
    

(3)使用

  • 在组件中使用

    import { useRecoilState, useRecoilValue } from "recoil";
    
    function TodoList() {
        const [todos, setTodos] = useRecoilState(todoAtom);
        const todoCount = useRecoilValue(todoCountSelector);
    
        return (
            <div>
                <h1>Todo Count: {todoCount}</h1>
                <ul>
                    {todos.map((todo, index) => (
                        <li key={index}>{todo}</li>
                    ))}
                </ul>
                <button onClick={() => setTodos([...todos, "New Todo"])}>
                    Add Todo
                </button>
            </div>
        );
    }
    

(4)优点**

  • 细粒度管理:可以精确控制状态的更新。
  • 自动缓存:选择器的结果会被自动缓存,减少重复计算。
  • 与 React 高度集成:使用起来非常自然。

4. Jotai:简化版的 Recoil

核心概念: Jotai 是一个类似 Recoil 的状态管理库,但它更简化,API 更简洁。

(1)原子(Atom)

  • 定义:Jotai 也使用原子作为状态的基本单位。

  • 示例

    import { atom } from "jotai";
    
    const todoAtom = atom([]);
    

(2)使用

  • 在组件中使用

    import { useAtom } from "jotai";
    
    function TodoList() {
        const [todos, setTodos] = useAtom(todoAtom);
    
        return (
            <div>
                <ul>
                    {todos.map((todo, index) => (
                        <li key={index}>{todo}</li>
                    ))}
                </ul>
                <button onClick={() => setTodos([...todos, "New Todo"])}>
                    Add Todo
                </button>
            </div>
        );
    }
    

(3)优点**

  • API 简洁:比 Recoil 更简单,适合快速上手。
  • 轻量级:体积小,性能好。
  • 易于扩展:可以方便地与其他库结合使用。

总结

特点优点缺点
Redux通过 actionreducer 更新状态,结构清晰,适合复杂应用。功能强大,社区支持多。配置繁琐,代码量多。
Redux Toolkit简化了 Redux 的使用,内置 createSlicecreateAsyncThunk代码更简洁,支持异步操作。仍然需要理解 Redux 的核心概念。
Recoil基于原子和选择器,支持细粒度状态管理,自动缓存。细粒度管理,与 React 高度集成。API 较复杂,学习曲线稍陡。
Jotai类似 Recoil,但更简化,API 更简洁。API 简洁,轻量级,易于上手。功能相对有限,适合小型项目。