Redux 入门教程(三):React-Redux 的用法
React-Redux 是 Redux 的 React 绑定库,它可以让你的 React 组件从 Redux store 中读取数据,并且向 store 分发 actions 来更新数据。
一、UI 组件和容器组件
React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。
UI 组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用
this.state
)
- 所有数据都由参数(
props
)提供
- 不使用 Redux 的 API
容器组件
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
二、connect 方法
React-Redux 提供 connect
方法,用于从 UI 组件生成容器组件。
1 2 3 4 5 6
| import { connect } from 'react-redx';
const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList);
|
1. mapStateToProps
1 2 3 4 5
| const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) }; };
|
2. mapDispatchToProps
1 2 3 4 5 6 7
| const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)); } }; };
|
三、Provider 组件
Provider
组件可以让容器组件拿到 state
。
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { Provider } from 'react-redux'; import { createStore } from 'redux'; import todoApp from './reducers'; import App from './components/App';
let store = createStore(todoApp);
render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
|
四、实例:Todo 应用
1. 定义 Action Types
1 2 3
| export const ADD_TODO = 'ADD_TODO'; export const TOGGLE_TODO = 'TOGGLE_TODO'; export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
|
2. 创建 Action Creators
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export const addTodo = (text) => ({ type: ADD_TODO, text });
export const toggleTodo = (id) => ({ type: TOGGLE_TODO, id });
export const setVisibilityFilter = (filter) => ({ type: SET_VISIBILITY_FILTER, filter });
|
3. 实现 Reducer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const initialState = { visibilityFilter: 'SHOW_ALL', todos: [] };
function todoApp(state = initialState, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return Object.assign({}, state, { visibilityFilter: action.filter }); case ADD_TODO: return Object.assign({}, state, { todos: [ ...state.todos, { text: action.text, completed: false } ] }); case TOGGLE_TODO: return Object.assign({}, state, { todos: state.todos.map((todo, index) => { if (index === action.id) { return Object.assign({}, todo, { completed: !todo.completed }); } return todo; }) }); default: return state; } }
|
4. 创建容器组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) }; };
const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)); } }; };
const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList);
|
五、最佳实践
组件拆分:
- 将 UI 组件和容器组件分开
- 保持组件的单一职责
- 适当使用高阶组件
性能优化:
- 使用
shouldComponentUpdate
或 React.memo
- 避免不必要的重渲染
- 合理使用
useSelector
和 useDispatch
代码组织:
- 按功能模块组织代码
- 使用常量定义 action types
- 保持 reducer 的纯函数特性
调试技巧:
- 使用 Redux DevTools
- 添加适当的日志
- 使用 TypeScript 增加类型安全