React 之 Redux 第三十二节 Redux 常用API及HOOKS,以及Redux Toolkit核心API使用详解

发布于:2025-04-16 ⋅ 阅读:(40) ⋅ 点赞:(0)

一、4.X版本中核心 API 和用途

1. createStore(reducer, [preloadedState], [enhancer])

用途: 创建 Redux Store(数据仓库)
参数:
reducer: 状态更新函数
preloadedState: 初始状态(可选)
enhancer: 中间件增强器(如 applyMiddleware)

注意:
现代 Redux 推荐使用 @reduxjs/toolkit 的 configureStore 替代

import { createStore } from 'redux';

// 定义 reducer
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};

// 创建 store
const store = createStore(counterReducer);

2. combineReducers(reducers)

用途: 合并多个 reducer

import { combineReducers } from 'redux';

const userReducer = (state = {}, action) => { /* ... */ };
const postReducer = (state = [], action) => { /* ... */ };
const rootReducer = combineReducers({
  user: userReducer,
  posts: postsReducer
});

3. applyMiddleware(…middlewares)

用途: 应用中间件(如 redux-thunk, redux-saga)

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk, logger)
);

4. compose(…functions)

用途: 组合多个 Store 增强器

const enhancer = compose(applyMiddleware(thunk), devToolsExtension());

二、React-Redux 中常用 Hooks

1. useSelector(selector)

用途: Store 中提取状态

import { useSelector } from 'react-redux';

function Counter() {
  // ✅ 正确:直接返回原始值
  const count = useSelector(state => state.counter);

  // ❌ 错误:每次返回新对象会导致重复渲染
  const badSelector = useSelector(state => ({ 
    count: state.counter 
  }));
  
  return <div>{count}</div>;
}

注意事项:
避免返回新对象导致不必要的渲染(使用浅比较)
复杂选择器建议使用 reselect 库做缓存

2. useDispatch()

用途: 获取 dispatch 函数

import { useDispatch } from 'react-redux';

function Button() {
  const dispatch = useDispatch();

  // ✅ 正确:在事件处理中调用
  const handleClick = () => dispatch({ type: 'INCREMENT' });

  // ❌ 错误:避免在渲染中直接调用
  // dispatch({ type: 'BAD' }); 

  return <button onClick={handleClick}>+</button>;
}

注意事项:
避免在渲染中直接调用 dispatch(应在事件回调或 useEffect 中)

3. useStore()

用途: 直接访问 Store 对象(极少使用)

const store = useStore();

三、Redux Toolkit (RTK) 核心 API

1. configureStore(options)

用途: 简化 Store 创建(自动整合中间件和开发工具)

import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
  middleware: (getDefaultMiddleware) => [
    ...getDefaultMiddleware(),
    customMiddleware
  ],
  devTools: process.env.NODE_ENV !== 'production'
});

2. createSlice(options)

用途: 自动生成 action creators 和 reducer

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
    decrement: (state) => state - 1,
    // ✅ 使用 Immer 允许"突变"写法
    addBy: (state, action) => {
      state.value += action.payload; 
    }
  },
  // 异步处理 extraReducers
  extraReducers: (builder) => {
    builder.addCase(fetchUser.fulfilled, (state, action) => {
      state.user = action.payload;
    })
  }
});

// 自动生成 action creators
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
// 自动生成: counterSlice.actions.increment()
// 自动生成: counterSlice.reducer

3. createAsyncThunk(typePrefix, payloadCreator)

用途: 处理异步操作

import { createAsyncThunk } from '@reduxjs/toolkit';

export const fetchUser = createAsyncThunk(
  'user/fetch',
  async (userId, thunkAPI) => {
    try {
      const response = await fetch(`/api/users/${userId}`);
      return await response.json();
    } catch (error) {
      return thunkAPI.rejectWithValue(error.message);
    }
  }
);

// 在组件中使用
dispatch(fetchUser(123));
// 自动生成: fetchUser.pending/fulfilled/rejected 状态

4. createEntityAdapter()

用途: 标准化实体数据管理(如 CRUD 操作)

const usersAdapter = createEntityAdapter();
usersAdapter.addOne(state, user); // 自动处理标准化状态

四、注意事项

1. 不可变性 (Immutability)

必须: Reducer 中禁止直接修改原状态

正确做法:

// 错误: state.value = newValue
// 正确:
return { ...state, value: newValue };
// 或使用 RTK 的 Immer(自动处理不可变):
createSlice({
  reducers: {
    updateValue: (state, action) => {
      state.value = action.payload; // 允许直接修改(Immer 内部处理)
    }
  }
})

2. 性能优化

避免重复渲染: 使用 useSelector 时选择最小化状态

// 低效写法:每次返回新对象
const badSelect = useSelector(state => ({
  items: state.todos.items,
  filter: state.todos.filter
}));

// 高效写法:拆分选择器
const items = useSelector(state => state.todos.items);
const filter = useSelector(state => state.todos.filter);

// 或使用 reselect 记忆化
import { createSelector } from 'reselect';
const selectTodos = state => state.todos;
const selectTodoDetails = createSelector(
  [selectTodos],
  (todos) => ({
    items: todos.items,
    filter: todos.filter
  })
);

批量更新: 合并多个 dispatch 操作(如使用 redux-batched-actions)

3. 异步操作

中间件选择:
简单场景用 redux-thunk
复杂异步流用 redux-sagaredux-observable
错误处理: 在 createAsyncThunk 中捕获异常

// 在 extraReducers 中处理错误
const userSlice = createSlice({
  name: 'user',
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.data = action.payload;
        state.status = 'succeeded';
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.error = action.payload;
        state.status = 'failed';
      });
  }
});

4. 项目结构

推荐模式:
按功能模块组织代码(Ducks 模式)
使用 RTK Query 管理 API 请求
分离 UI 状态和业务状态

五、总结建议

优先使用 Redux Toolkit 替代传统 Redux(减少样板代码)
严格遵循不可变性原则:永远不直接修改 state(除非在 RTK 的 createSlice 中)
合理拆分 :reducer 避免单个文件过大
异步操作规范化: 使用 createAsyncThunk 或中间件
性能敏感场景 使用记忆化选择器(reselect
通过结合 React Hooks 和 Redux Toolkit,可以显着简化 Redux 的使用复杂度,同时保持代码的可维护性和性能。


网站公告

今日签到

点亮在社区的每一天
去签到