前言
参考
探究的问题:
将React和Redux绑定的两种方式
- react-redux中的useSelector、useDispatch钩子
- Connect API,mapStateToProps,mapDispatchToProps
将React和Redux绑定
两种方法,
新的技术栈,Hook API:useSelector、useDispatch
旧的技术栈,Connect API:mapStateToProps,mapDispatchToProps
这两种方法的Connect API和Hook API都由react-redux库提供
React Redux
文档:https://react-redux.js.org/api/
翻译版:https://zhuanlan.zhihu.com/p/81569230
添加依赖
1 | cnpm install --save redux |
react-redux库中提供了useSelector、useDispatch钩子和connect API
redux-devtools-extension库可以激活redux devtools插件,对redux进行调试
Hooks API
基本理念:在React中,不直接操作store对象,而是通过钩子获取state派发事件
Provider
要在应用中获取store,需要使用Provider将应用包起来,使store暴露在组件树中,之后才能用其他Hooks管理Redux store
1 | import { createStore } from "redux"; |
useSelector
1 | const result : any = useSelector(selector : Function, equalityFn? : Function) |
入参
- selector,回调函数,输入为state树,输出为想要获取的state树的一部分
返回值
- 回调的输出
原理
- 监听原理:每一个useSelector都对store创建了一个独立的subscription(store.subscribe),监听了state变化
- 渲染时机:函数组件渲染时,执行selector,useSelector存储了上一次selector的返回值,与这一次的比较,不同才触发重新渲染
- 此处的比较是
===
严格引用比较
- 此处的比较是
1 | import { useSelector } from 'react-redux' |
useDispatch
1 | const dispatch = useDispatch() |
返回 Redux store 的 分发(dispatch) 函数的引用。等价于:
1 | // react redux里不会这么用,只是意会一下 |
调用dispatch函数派发action
1 | import {increment, decrement, set} from './actions' |
在这里同样可以用redux中的bindActionCreators来集成action creator,上面的语法和下面的等价
1 | const actions=bindActionCreators( |
练习
需求
原sandbox:https://codesandbox.io/s/uo1rb
需求:
按钮:按下按钮改变count的值
- INCREMENT,+1
- DECREMENT,-1
- RESET,-> 0
输入框:
- count改变时输入框的值也改变
- 按下提交后将count值置为输入框的值
代码
mysandbox:https://codesandbox.io/s/redux-counter-zbtugj
使用useCounter封装获取dispatch,action和state的操作
1 | import { useSelector, useDispatch } from 'react-redux'; |
表单组件中,表单、输入框、store的state、组件的state、输入框的value的绑定逻辑
- store的state、组件的state互相影响
- store的state改变引起组件state改变(useEffect)
- 表单提交时使用组件state改变store的state(onSubmit)
- 组件的state、输入框的value双向绑定
- 组件state改变引起输入框value改变(value={value})
- 输入框value被用户改写时引起state改变(onChange)
1 | import { useEffect, useState } from 'react'; |
Connect API
connect
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
连接 React 组件与 Redux store。
const NewComponent=connect(...)(OldComponent)
连接操作不会改变原来的组件类。
反而返回一个新的已与 Redux store 连接的组件类。
一般只会用到前两个参数:mapStateToProps,mapDispatchToProps
注* 如果一个参数都不传,默认会把dispatch传入到组件中作为参数
1 | const NewComponent = connect()(OldComponent); |
mapStateToProps
mapStateToProps(state, [ownProps]): stateProps
(Function)
作为connect函数的第一个参数,如果不传这个参数,或者传入null,组件就不会监听store
如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps
函数就会被调用。
入参:
- state,值为store中存储的state树
- ownProps,可省略,值为传递到组件的 props,
- ,如果传了ownProps,那么只要组件接收到新的 props,
mapStateToProps
也会被调用(例如,当 props 接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算)
- ,如果传了ownProps,那么只要组件接收到新的 props,
返回值:
- stateProps,必须返回一个纯对象,这个对象会与组件的 props 合并
例子:
MenuItems需要接收store中的state.items作为输入参数
在不改变原组件的基础上,使用connect返回一个新组件,新组建能够监听store的变化,并将state.items输入到原组件
1 | import { connect } from 'react-redux'; |
mapDispatchToProps
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
(Object or Function)
mapStateToProps可以是一个对象:
- 每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;
- 每个方法将返回一个新的函数,函数中
dispatch
方法会将action creator的返回值作为参数执行。 - 这些属性会被合并到组件的 props 中。
mapStateToProps可以是一个函数:
入参:
dispatch
,函数,就是useDispatch产生的dispatch函数ownProps
,可省略,该参数的值为传递到组件的 props,- 如果传了ownProps,只要组件接收到新 props,
mapDispatchToProps
也会被调用。
- 如果传了ownProps,只要组件接收到新 props,
返回值:对象,
- 这个对象通过
dispatch
函数与 action creator 以某种方式绑定在一起 - (提示:你也许会用到 Redux 的辅助函数
bindActionCreators()
)。
如果省略这个 mapDispatchToProps
参数,默认情况下,dispatch
会自动注入到组件 props 中。
例子:
NewItemForm需要一个函数submitHandler作为它的输入参数,
- 并且这个 submitHandler 要跟 dispatch 绑定,调用即可派发action
- 需要派发的 action 由 addNewItem 生成
1 | import { connect } from 'react-redux'; |
也可以简写,只传对象,connect会自动把dispatch包到对象外面(bindActionCreators):
1 | const mapDispatchToProps = { |
练习
需求
原sandbox:https://codesandbox.io/s/df1j4
应用实现的功能是商品价格计算器
- 可以输入商品名和商品价格添加商品
- 可以删除商品
- 可以修改商品价格和数量
- 可以选择小费比例
- 最终计算出商品价格,小费价格和总价格
state树结构:
1 | { |
给按钮绑定事件:
- add item:将表单中的item加入到state.items
- remove:去掉state.items中对应的item
给每一个商品的价格和数量输入框绑定事件:
- 修改后更新state.items中的对应item
每一次产生上述修改后要重新计算总价
代码
我的sandbox:https://codesandbox.io/s/redux-calculator-mqt8g8
商品增删改
state.items相关的action creator
1 | export const ITEM_ADDED = 'ITEM_ADDED'; |
处理state.items部分更新的reducer
1 | import { |
删除、更新商品时如何派发action
1 | import { MenuItem } from '../components/MenuItem'; |
MenuItem的API如下:
1 | export const MenuItem = ({ |
商品价格计算
商品价格的计算结果没必要放到redux store里,直接从state中取值,计算后放入组件就行了
1 | import { connect } from 'react-redux'; |
Summary组件的API如下
1 | export const Summary = ({ subtotal = 0, tipAmount = 0, total = 0 }) => { |