Redux
redux是什么
-
redux是一个专门用于做状态管理的JS库(不是react插件库)。
-
它可以用在react, angular, vue等项目中, 但基本与react配合使用。
-
作用: 集中式管理react应用中多个组件共享的状态。
什么情况下需要使用redux
-
某个组件的状态,需要让其他组件可以随时拿到(共享)。
-
一个组件需要改变另一个组件的状态(通信)。
-
总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
使用 redux
安装: yarn add redux
精简版 - 求和案例
-
去除 Count 自身组件状态
-
src 下建立目录
- redux
- store.js
- count_reducer.js
- redux
-
store.js
- 引入 redux 中的 createStore 函数, 创建一个 store
- createStore 需要传入一个 reducer
- 默认暴露 store 对象
-
count_reducer.js
- reducer 的本质是一个函数, 接收: preState, action 两个参数, 返回加工后的状态
- reducer 有两个作用: 初始化状态, 加工状态
- reducer 被第一次调用时, 是 store 自动触发的
- 传递的 preState 是 undefined
- 传递的 action 是随机生成的 type
-
在 index.js 中检测 store 中状态的改变, 一旦发生改变, 重选渲染
<App/>
- subscribe 函数在 redux 中状态改变的时候后触发.
- 因为 redux 只负责管理状态, 没办法通知 react 去重新渲染页面, 只能间接触发.
完整版 - 求和案例
新增文件:
-
count_action.js 用来专门创建 action 对象, 直接在 需要 dispatch 的地方当做参数传入.
-
constant.js 用来容易写错的 type 值
异步版 - 求和案例
-
需要提前引入一个中间件来使用异步:
yarn add redux-thunk
,- 在 store.js 中
import thunk from 'redux-thunk'
-
action 作为一个对象, 可以是
{type:'type',data:data }
, 也可以是一个函数. 当它是函数的时候, 就被称为异步 action. -
store 的 dispatch 可以做判断:
-
如果传入的是同步 action, 那么直接 dispatch 这个对象给 reducer 用.
-
如果传入的是异步 action, 那么会给这个函数传入一个 dispatch 函数, 让异步函数来完成及时或者 ajax 等异步工作后, 调用 dispatch , dispatch 需要传入一个同步 action 函数.
-
react-redux
1. 明确两个概念:
-
UI组件
-
只负责 UI 的呈现,不带有任何业务逻辑
-
通过props接收数据(一般数据和函数)
-
不使用任何 Redux 的 API
-
一般保存在components文件夹下
-
-
容器组件
-
负责管理数据和业务逻辑,不负责UI的呈现
-
使用 Redux 的 API, 和 redux 通信, 将结果给 UI 组件
-
一般保存在containers文件夹下
-
2. 如何创建容器组件
靠 react-redux 的 connect 函数
connect(mapStateToProps, mapDispatchToProps)(UI组件)
- mapStateToProps: 映射状态, 返回值是一个对象{key:state}
- mapDispatchToProps: 映射操作状态的方法, 返回值是一个对象{key:function}
- 可以作为一个对象直接传, 对象就是操作状态的方法.
- 由 react-redux 来调用 dispatch, 自己不用写(不然要在参数里传 dispatch, 然后在返回的对象里调用 dispatch)
3. 容器组件中的 store
是靠 props 传进去的, 而不是在容器中直接引入的.
react-redux 优化版本
-
容器组件和 UI 组件混合成一个组件, UI 组件作为类通过 connect 传入容器, 然后暴露容器
-
无需自己给容器传递 store, 不然多个容器每次都要在props键值对传同样的
store={store}
, 属于重复性代码. 只需要给<App />
外侧包裹一个<Provider store={store}>
, 就可以完成对所有容器的 store 传递. 主要要提前引入 Providerimport {Provider} from 'react-redux';
-
使用 react-redux 后也不需要自己
subscribe
redux 中的状态的改变了, 容器组件可以自动完成这个工作. -
mapDispatchToProps
可以简写成一个对象, 不用每次传入 dispatch 并返回函数. -
一个组件和 redux 的交互步骤
-
定义 UI 组件 – 不暴露 no leakage
-
引入 connect 生成一个容器组件, 并暴露
connect(state => ({key:value}) {key:xxxAction} )(UI 组件)
-
在 UI 组件中通过 this.props.xxx 来读写状态
-
数据共享
- 定义一个 Person 组件, 和 Count 组件通过 redux 共享数据
- 为 Person 组件同样写一套 reducer, action, 并配置 constant 常量
- 重点: Person 的 reducer 和 Count 的 Reducer 要使用 combineReducer 进行合并, 合并后的总状态是一个对象.
- 交给 store 的是 reducer, 最后注意在组件去除状态的时候, 状态为一个对象, 所以要取到位.
reducer 必须是纯函数
纯函数就是说同样的输入必须得到同样的输出.
1. 函数体内不能改写参数;
// 不纯
function demo(a) {
a = 9
}
- 不能有网络请求和 IO.
- 不能调用 Date.now()或者 Math.random()等不纯的方法.
- redux 的 reducer 函数必须是一个纯函数.
关于 prestate 数组的更新, 返回的 state 必须有一个新的数组.
swith(type) {
case ADD_PERSON:
return [data, ...prestate]
}
js 的数组应该是一个地址, 指向堆的存储区域. 如果 prestate 更改前后, 只是数值边了, 地址没变. store 识别不出来, 页面不重新渲染.
一个数组 arr 直接往里推一个单位, arr 的地址是不变的.
let arr = [1,3,5,7,9]
arr.push(10)
最终版
需要把所有 reducer 先引入到 reducer 文件夹下的 index.js, 然后在 store 中引入 index.js
查看项目请点击
Redux 开发者工具
-
先在 Chrome 安装扩展
-
在 react 开发目录下,
yarn add redux-devtools-extension
. -
在 store.js 中编写
//创建 rudux 中最为核心的 store 对象
import {createStore, applyMiddleware, combineReducers} from 'redux'
//不加花括号是因为默认暴露了
import countReducer from './reducers/count'
import personReducer from './reducers/person'
//引入 redux-thunk 用于支持异步 action
import thunk from 'redux-thunk'
//引入 redux 开发者工具
import {composeWithDevTools} from 'redux-devtools-extension'
//汇总所有 reducer
const allReducer = combineReducers({
count:countReducer,
person:personReducer
})
//默认暴露一个 store, 注意使用 redux开发者工具的调用方式
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))