Redux基础 #

一、Redux概述 #

1.1 什么是Redux #

Redux 是一个可预测的状态容器,基于 Flux 架构思想,用于管理应用状态。

1.2 三大原则 #

原则 说明
单一数据源 整个应用的状态存储在一个store中
State只读 只能通过dispatch action来修改状态
纯函数修改 Reducer是纯函数,接收旧状态和action,返回新状态

1.3 数据流 #

text
Action → Reducer → Store → View
   ↑                              │
   └──────────────────────────────┘

二、核心概念 #

2.1 Action #

Action 是描述事件的普通对象:

javascript
// Action类型常量
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_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 }
  };
}

2.2 Reducer #

Reducer 是纯函数,处理状态变化:

javascript
const initialState = {
  todos: [],
  filter: 'all'
};

function todoReducer(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.id
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };

    case 'REMOVE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload.id)
      };

    case 'SET_FILTER':
      return {
        ...state,
        filter: action.payload
      };

    default:
      return state;
  }
}

2.3 Store #

Store 是保存状态的地方:

javascript
import { createStore, combineReducers } from 'redux';

// 合并多个reducer
const rootReducer = combineReducers({
  todos: todoReducer,
  user: userReducer
});

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

// 获取状态
console.log(store.getState());

// 订阅变化
const unsubscribe = store.subscribe(() => {
  console.log('State changed:', store.getState());
});

// 派发action
store.dispatch(addTodo('Learn Redux'));

// 取消订阅
unsubscribe();

三、在React中使用Redux #

3.1 安装 #

bash
npm install redux react-redux

3.2 Provider组件 #

javascript
import { Provider } from 'react-redux';
import { createStore } from 'redux';

const store = createStore(rootReducer);

function App() {
  return (
    <Provider store={store}>
      <TodoApp />
    </Provider>
  );
}

3.3 useSelector获取状态 #

javascript
import { useSelector } from 'react-redux';

function TodoList() {
  // 获取整个状态
  const state = useSelector(state => state);
  
  // 获取特定状态
  const todos = useSelector(state => state.todos);
  const filter = useSelector(state => state.filter);

  // 计算派生状态
  const filteredTodos = useSelector(state => {
    switch (state.filter) {
      case 'active':
        return state.todos.filter(t => !t.completed);
      case 'completed':
        return state.todos.filter(t => t.completed);
      default:
        return state.todos;
    }
  });

  return (
    <ul>
      {filteredTodos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

3.4 useDispatch派发Action #

javascript
import { useDispatch, useSelector } from 'react-redux';
import { addTodo, toggleTodo, removeTodo } from './actions';

function TodoApp() {
  const dispatch = useDispatch();
  const todos = useSelector(state => state.todos);
  const [input, setInput] = useState('');

  const handleAdd = () => {
    if (input.trim()) {
      dispatch(addTodo(input));
      setInput('');
    }
  };

  const handleToggle = (id) => {
    dispatch(toggleTodo(id));
  };

  const handleRemove = (id) => {
    dispatch(removeTodo(id));
  };

  return (
    <div>
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
        onKeyPress={(e) => e.key === 'Enter' && handleAdd()}
      />
      <button onClick={handleAdd}>添加</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span
              onClick={() => handleToggle(todo.id)}
              style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
            >
              {todo.text}
            </span>
            <button onClick={() => handleRemove(todo.id)}>删除</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

四、异步Action #

4.1 安装redux-thunk #

bash
npm install redux-thunk

4.2 配置中间件 #

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

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

4.3 创建异步Action #

javascript
// 同步Action
const fetchUsersRequest = () => ({ type: 'FETCH_USERS_REQUEST' });
const fetchUsersSuccess = (users) => ({
  type: 'FETCH_USERS_SUCCESS',
  payload: users
});
const fetchUsersFailure = (error) => ({
  type: 'FETCH_USERS_FAILURE',
  payload: error
});

// 异步Action (Thunk)
export const fetchUsers = () => {
  return async (dispatch) => {
    dispatch(fetchUsersRequest());
    
    try {
      const response = await fetch('/api/users');
      const users = await response.json();
      dispatch(fetchUsersSuccess(users));
    } catch (error) {
      dispatch(fetchUsersFailure(error.message));
    }
  };
};

4.4 在组件中使用 #

javascript
function UserList() {
  const dispatch = useDispatch();
  const { users, loading, error } = useSelector(state => state.users);

  useEffect(() => {
    dispatch(fetchUsers());
  }, [dispatch]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

五、项目结构 #

5.1 按功能组织 #

text
src/
├── actions/
│   ├── todoActions.js
│   └── userActions.js
├── reducers/
│   ├── todoReducer.js
│   ├── userReducer.js
│   └── index.js
├── store/
│   └── index.js
└── components/
    ├── TodoList.jsx
    └── UserList.jsx

5.2 Ducks模式 #

javascript
// features/todos.js

// Actions
const ADD_TODO = 'todos/ADD_TODO';
const TOGGLE_TODO = 'todos/TOGGLE_TODO';

// Action Creators
export const addTodo = (text) => ({
  type: ADD_TODO,
  payload: { id: Date.now(), text, completed: false }
});

export const toggleTodo = (id) => ({
  type: TOGGLE_TODO,
  payload: { id }
});

// Reducer
const initialState = [];

export default function todosReducer(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return [...state, action.payload];
    case TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    default:
      return state;
  }
}

// Selectors
export const selectAllTodos = (state) => state.todos;
export const selectActiveTodos = (state) =>
  state.todos.filter(t => !t.completed);

六、最佳实践 #

6.1 使用常量定义Action类型 #

javascript
// actionTypes.js
export const ADD_TODO = 'todos/ADD_TODO';
export const TOGGLE_TODO = 'todos/TOGGLE_TODO';
export const REMOVE_TODO = 'todos/REMOVE_TODO';

6.2 使用选择器 #

javascript
// selectors.js
export const selectTodos = (state) => state.todos;
export const selectFilter = (state) => state.filter;

export const selectFilteredTodos = createSelector(
  [selectTodos, selectFilter],
  (todos, filter) => {
    switch (filter) {
      case 'active':
        return todos.filter(t => !t.completed);
      case 'completed':
        return todos.filter(t => t.completed);
      default:
        return todos;
    }
  }
);

// 使用
function TodoList() {
  const filteredTodos = useSelector(selectFilteredTodos);
  // ...
}

6.3 使用Redux DevTools #

javascript
import { createStore, applyMiddleware, compose } from 'redux';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

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

七、总结 #

概念 说明
Store 状态容器
Action 描述事件的对象
Reducer 纯函数处理状态变化
Dispatch 派发Action
Selector 获取状态的函数

核心原则:

  • 单一数据源
  • State只读
  • 纯函数修改状态
最后更新:2026-03-26