当前位置: 首页 > news >正文

Redux完整指南:从入门到精通

Redux完整指南:从入门到精通


文章目录

  • `Redux完整指南:从入门到精通`
    • 什么是Redux?
    • 为什么需要Redux?
    • Redux的三大核心原则
      • 1. 单一数据源(Single Source of Truth)
      • 2. 状态是只读的(State is Read-Only)
      • 3. 使用纯函数来执行修改(Changes are Made with Pure Functions)
    • Redux的核心概念
      • Action(动作)
      • Reducer(归约器)
      • Store(存储)
    • Redux的数据流
    • Redux与React的集成
      • react-redux库
    • Redux中间件
      • redux-thunk
      • redux-saga
    • 最佳实践
      • 1. 状态结构设计
      • 2. 使用combineReducers
      • 3. 规范化状态结构
      • 4. 使用Redux Toolkit (RTK)
    • 性能优化
      • 1. 选择器优化
      • 2. 组件优化
    • 调试工具
      • Redux DevTools
    • 何时使用Redux
      • 适合使用Redux的场景:
      • 不适合使用Redux的场景:


在这里插入图片描述

什么是Redux?

Redux是一个可预测的JavaScript状态容器,主要用于管理应用程序的状态。它由Dan Abramov和Andrew Clark于2015年创建,灵感来源于Flux架构和Elm语言。Redux遵循单向数据流的设计模式,使得应用状态的变化变得可预测和易于调试。

为什么需要Redux?

在现代前端应用中,随着应用规模的增长,组件间的状态共享变得越来越复杂。传统的状态管理方式可能导致以下问题:

  • 状态分散:状态散布在各个组件中,难以追踪
  • 状态传递复杂:深层组件需要通过多层传递获取状态
  • 状态更新不一致:多个组件修改同一状态时容易产生冲突
  • 调试困难:状态变化难以追踪和回溯

Redux通过集中化状态管理解决了这些问题。

Redux的三大核心原则

1. 单一数据源(Single Source of Truth)

整个应用的状态被储存在一个对象树中,这个对象树只存在于唯一的store中。

// 整个应用只有一个store
const store = createStore(rootReducer);

2. 状态是只读的(State is Read-Only)

改变状态的唯一方法是触发action,action是一个描述已发生事件的普通对象。

// 错误的做法
state.todos.push(newTodo);// 正确的做法
dispatch({type: 'ADD_TODO',payload: newTodo
});

3. 使用纯函数来执行修改(Changes are Made with Pure Functions)

为了描述action如何改变状态树,需要编写reducers,这些都是纯函数。

// 纯函数reducer
function todosReducer(state = [], action) {switch (action.type) {case 'ADD_TODO':return [...state, action.payload];default:return state;}
}

Redux的核心概念

Action(动作)

Action是把数据从应用传到store的有效载荷,是store数据的唯一来源。

// Action类型常量
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const DELETE_TODO = 'DELETE_TODO';// Action创建函数
function addTodo(text) {return {type: ADD_TODO,payload: {id: Date.now(),text,completed: false}};
}function toggleTodo(id) {return {type: TOGGLE_TODO,payload: id};
}function deleteTodo(id) {return {type: DELETE_TODO,payload: id};
}

Reducer(归约器)

Reducer指定了应用状态的变化如何响应actions并发送到store。

// 初始状态
const initialState = {todos: [],filter: 'ALL'
};// 根reducer
function rootReducer(state = initialState, action) {switch (action.type) {case ADD_TODO:return {...state,todos: [...state.todos, action.payload]};case TOGGLE_TODO:return {...state,todos: state.todos.map(todo =>todo.id === action.payload? { ...todo, completed: !todo.completed }: todo)};case DELETE_TODO:return {...state,todos: state.todos.filter(todo => todo.id !== action.payload)};default:return state;}
}

Store(存储)

Store是Redux应用中的状态容器,负责:

  • 维持应用的state
  • 提供getState()方法获取state
  • 提供dispatch(action)方法更新state
  • 通过subscribe(listener)注册监听器
import { createStore } from 'redux';// 创建store
const store = createStore(rootReducer);// 获取当前状态
console.log(store.getState());// 订阅状态变化
const unsubscribe = store.subscribe(() => {console.log('状态更新:', store.getState());
});// 派发action
store.dispatch(addTodo('学习Redux'));
store.dispatch(toggleTodo(1));// 取消订阅
unsubscribe();

Redux的数据流

Redux遵循严格的单向数据流:

  1. 用户交互触发Action → 用户点击按钮或输入数据
  2. 派发Action到Storestore.dispatch(action)
  3. Store调用Reducerreducer(currentState, action)
  4. Reducer返回新状态 → 纯函数计算新状态
  5. Store保存新状态 → 状态树更新
  6. 通知订阅者 → 组件重新渲染
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Action    │───▶│    Store    │───▶│   Reducer   │
└─────────────┘    └─────────────┘    └─────────────┘▲                   │                   ││                   ▼                   ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ User Input  │    │  Subscribe  │    │  New State  │
└─────────────┘    └─────────────┘    └─────────────┘

Redux与React的集成

react-redux库

react-redux提供了Redux与React的绑定,主要包括:

import React from 'react';
import { Provider, connect, useSelector, useDispatch } from 'react-redux';
import { createStore } from 'redux';// 1. 使用Provider包装应用
function App() {return (<Provider store={store}><TodoApp /></Provider>);
}// 2. 使用Hooks方式(推荐)
function TodoList() {const todos = useSelector(state => state.todos);const dispatch = useDispatch();const handleAddTodo = (text) => {dispatch(addTodo(text));};return (<div>{todos.map(todo => (<div key={todo.id}><span>{todo.text}</span><button onClick={() => dispatch(toggleTodo(todo.id))}>{todo.completed ? '✓' : '○'}</button><button onClick={() => dispatch(deleteTodo(todo.id))}>删除</button></div>))}</div>);
}// 3. 使用connect方式(较旧)
const mapStateToProps = (state) => ({todos: state.todos
});const mapDispatchToProps = {addTodo,toggleTodo,deleteTodo
};const ConnectedTodoList = connect(mapStateToProps,mapDispatchToProps
)(TodoList);

Redux中间件

中间件提供了在action被发送到reducer之前进行拦截的能力。

redux-thunk

处理异步操作的中间件:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';const store = createStore(rootReducer, applyMiddleware(thunk));// 异步action创建函数
function fetchTodos() {return async (dispatch, getState) => {dispatch({ type: 'FETCH_TODOS_START' });try {const response = await fetch('/api/todos');const todos = await response.json();dispatch({ type: 'FETCH_TODOS_SUCCESS', payload: todos });} catch (error) {dispatch({ type: 'FETCH_TODOS_ERROR', payload: error.message });}};
}

redux-saga

使用生成器函数处理副作用:

import { call, put, takeEvery } from 'redux-saga/effects';function* fetchTodosSaga() {try {const todos = yield call(fetch, '/api/todos');yield put({ type: 'FETCH_TODOS_SUCCESS', payload: todos });} catch (error) {yield put({ type: 'FETCH_TODOS_ERROR', payload: error.message });}
}function* watchFetchTodos() {yield takeEvery('FETCH_TODOS_REQUEST', fetchTodosSaga);
}

最佳实践

1. 状态结构设计

// 推荐的状态结构
const initialState = {// 按功能模块组织todos: {items: [],loading: false,error: null},user: {profile: null,isAuthenticated: false},ui: {sidebarOpen: false,theme: 'light'}
};

2. 使用combineReducers

import { combineReducers } from 'redux';const todosReducer = (state = { items: [], loading: false }, action) => {// 处理todos相关的action
};const userReducer = (state = { profile: null }, action) => {// 处理user相关的action
};const rootReducer = combineReducers({todos: todosReducer,user: userReducer
});

3. 规范化状态结构

// 避免嵌套过深的数据结构
const normalizedState = {todos: {byId: {1: { id: 1, text: '学习Redux', completed: false },2: { id: 2, text: '写博客', completed: true }},allIds: [1, 2]}
};

4. 使用Redux Toolkit (RTK)

Redux Toolkit是官方推荐的现代Redux工具集:

import { createSlice, configureStore } from '@reduxjs/toolkit';// 使用createSlice简化reducer和action的创建
const todosSlice = createSlice({name: 'todos',initialState: [],reducers: {addTodo: (state, action) => {// 可以直接修改state(内部使用Immer)state.push(action.payload);},toggleTodo: (state, action) => {const todo = state.find(todo => todo.id === action.payload);if (todo) {todo.completed = !todo.completed;}}}
});// 自动生成action创建函数
export const { addTodo, toggleTodo } = todosSlice.actions;// 配置store
const store = configureStore({reducer: {todos: todosSlice.reducer}
});

性能优化

1. 选择器优化

import { createSelector } from 'reselect';// 创建记忆化选择器
const selectTodos = state => state.todos;
const selectFilter = state => state.filter;const selectVisibleTodos = createSelector([selectTodos, selectFilter],(todos, filter) => {switch (filter) {case 'COMPLETED':return todos.filter(todo => todo.completed);case 'ACTIVE':return todos.filter(todo => !todo.completed);default:return todos;}}
);

2. 组件优化

import React, { memo } from 'react';
import { useSelector, shallowEqual } from 'react-redux';// 使用memo避免不必要的重渲染
const TodoItem = memo(({ todo, onToggle, onDelete }) => {return (<div><span>{todo.text}</span><button onClick={() => onToggle(todo.id)}>切换</button><button onClick={() => onDelete(todo.id)}>删除</button></div>);
});// 使用shallowEqual进行浅比较
const TodoList = () => {const todos = useSelector(state => state.todos, shallowEqual);// ...
};

调试工具

Redux DevTools

Redux DevTools Extension提供了强大的调试功能:

const store = createStore(rootReducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

主要功能包括:

  • 时间旅行调试
  • Action重放
  • 状态快照
  • 性能监控

何时使用Redux

适合使用Redux的场景:

  • 应用有大量状态需要在多个组件间共享
  • 状态更新逻辑复杂
  • 需要时间旅行调试
  • 团队规模较大,需要统一的状态管理规范

不适合使用Redux的场景:

  • 简单的应用,状态较少
  • 状态主要是UI相关的本地状态
  • 学习成本与收益不成正比的小项目

http://www.xdnf.cn/news/953083.html

相关文章:

  • 群创3.5寸液晶模组LQ035NC111参数资料
  • PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
  • 冯诺依曼架构是什么?
  • 在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
  • 前端与服务器交互以及前端项目组成。
  • 2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
  • 【Proteus仿真】【32单片机-A011】HX711电子秤系统设计
  • BIO、NIO、AIO的区别
  • EtherCAT主站转Profinet网关接IS620N伺服驱动器与西门子plc通讯案例
  • Qt Http Server模块功能及架构
  • 【Java多线程从青铜到王者】单例设计模式(八)
  • markdown,nodejs前世今生以及内置模块相关知识点
  • AI原生应用实战:用户画像建模的7种机器学习方法
  • 力扣面试150题--蛇梯棋
  • 开发Vue.js组件的二三事
  • if 选择结构
  • 下载https协议的网络图片,并转为Base64
  • 浅谈非理想性因素对星座图的影响
  • ArcGIS Pro制作水平横向图例+多级标注
  • PIN码vs密码,电脑登录的快捷键你用对了吗?
  • CppCon 2015 学习:STL Algorithms in Action
  • Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
  • The Trade Desk推出DealDesk,试图让交易ID不再糟糕
  • HTTP 与 TCP 协议的区别与联系
  • 【C++】unordered_set和unordered_map
  • tauri项目,如何在rust端读取电脑环境变量
  • 画质MxPro:优化手游体验,畅享高清画质
  • Linux初步介绍
  • 【VLNs篇】07:NavRL—在动态环境中学习安全飞行
  • 多轮对话实现