安装:
yarn add redux react-redux
yarn add redux-devtools --dev
三大原则:
- 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
- State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
- 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers。
使用:
1. 定义 store
-
编写 reducer :一个 reducer 就是一个函数,跟 react 里面的 useReducer hook 里的 reducer 其实是一样的,都是接收第一个参数为 state, 第二个参数为 action, 最后返回一个新的 state。action 参数就是描述如何改变 store 里面的数据的对象,第一个参数 state 是 store 里面的整个 state 或 state 的一部分,reducer 函数根据不同的 action 对象对 state 进行处理,最后返回一个经过处理后的 state, 以此表示对整个 state 或 state 的一部分 的修改。react 强调 reducer 永远不要做以下操作:
(1) 修改传入参数;
(2) 执行有副作用的操作,如 API 请求和路由跳转;
(3) 调用非纯函数,如 Date.now() 或 Math.random()。
并且遵循: 只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
技巧:因为 reducer 必须返回一个 state(遇到未知的 action 时,一定要返回旧的 state),因此 reducer 的默认参数使用 es6 的默认 state 将精简代码。
总结:因为一个应用的 state 可能很大,由几个部分的功能组成,此时可以根据功能将 state 拆解为多个 reducer 处理,每个 reducer 必须返回一个 state, 由于返回的 state 的结构是具体的,多个 reducer 返回的 state 的组合就是整个应用的 state,无需再另外定义应用的 state 结构,它已经由各个 reducer 所返回的 state 定义。 -
将多个 reducer 组合起来
定义好所有需要的 reducer 后,就应该将它们组合起来,形成一个主 reducer,例如一个应用的主 reducer 可能类似于这样:
function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
它表示整个应用的 state 对象由 visibilityFilter 和 todos 部分组成(当然实际的应用可能远远不止这么简单)。reducer visibilityFilter 和 reducer todos 的调用分别返回了各自的部分。这种应用的 state 属性名和响应的 reducer 函数名一致时,可以借助于 react-redux 的 combineReducers 简化代码:
const todoApp = combineReducers({
todos,
visibilityFilter
})
最终,todoApp就是我们整个应用的 state。
2. 将 store 与 react 组件关联起来
- 根据 state 创建出整个应用是 store,将其作用在应用的根组件上:
let store = createStore(todoApp)
<Provider store={store}>
<App />
</Provider>
- 为了避免不必要的重复渲染,可以将应用分为:容器组件 和 展示组件:
(1)展示组件:这些组件只定义外观并不关心数据来源和如何改变。传入什么就渲染什么。如果你把代码从 Redux 迁移到别的架构,这些组件可以不做任何改动直接使用。它们并不依赖于 Redux。
(2)容器组件:负责把展示组件连接到 Redux,具体的方法就是调用 react-redux 的 connect 函数,它主要接收两个参数:
I: 第一个参数为一个函数,用于定义怎么将整个应用的 state 映射为展示组件的 props,这个函数接收两个参数,第一个是整个应用的 state, 第二个是该展示组件自身的 props,返回一个对象,该对象的根属性将成为展示组件的 props (数据)属性。
II: 第二个参数也是一个函数,用于定义怎么将整个应用的 dispatch 调用成为展示组件的 props,这个函数接收两个参数,第一个是整个应用的 store 的 dispatch 方法。
第二个是展示组件自己的props, 返回一个对象,该对象的根属性将成为展示组件的 props (方法)属性。
connect 返回一个函数,这个函数以展示组件作为参数调用后返回一个新的组件,这个组件就是展示组件连接到 Redux 之后的组件。
其它
- 技术上讲,容器组件就是使用 store.subscribe() 从 Redux state 树中读取部分数据,并通过 props 来把这些数据提供给要渲染的组件。你可以手工来开发容器组件,但建议使用 React Redux 库的 connect() 方法来生成,这个方法做了性能优化来避免很多不必要的重复渲染。使用 connect() 前,需要先定义 mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props 中。
- 除了读取 state,容器组件还能分发 action。类似的方式,可以定义 mapDispatchToProps() 方法接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法。
实操总结:
- 有时候卡了,要关闭 devServer 重新 yarn start
- 所有的 action 不管简单与复杂,都要放在相对统一的地方
- 关于 connet 函数第二个参数:要不就不传(传null),所有的展示组件里面直接调用 props.dispatch 完成action; 要不就全部用 mapDispatchToProps 返回的对象属性,如果传入了 mapDispatchToProps, 则展示组件的 props.dispatch 将为 undefined,否则会暴露 props.dispatch 方法。
- 如果有触发一个 action,每个 action 匹配多个 reducer 的 action 的 "type",则所有匹配的 action 都会触发
redux api:
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import { combineReducers } from 'redux'
import { connect } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
网友评论