1、为什么要学习redux?

随着Javascript的应用开发日趋复杂,javascript需要管理比任何时候都要多的state(状态)。

  • javascript需要管理的状态越来越多,越来越复杂;
  • 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等,也包括一些UI的状态,比如某些元素是否被选中,是否显示加载动态,当前分页的数据。

管理不断变化的state状态是非常困难的:

  • 状态之间相互存在依赖,一个状态的变化会引起另一个状态的变化,view页面也有可能会引起状态的变化。

2、redux是什么?

:::info Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。redux的工作原理汝下图所示: ::: redux.png

在Redux中,所有的数据(比如state)被保存在一个被称为store的容器中 → 在一个应用程序中只能有一个。store本质上是一个状态树,保存了在应用中需要保存的状态。任何UI组件都可以直接从store访问特定对象的状态。要通过本地或远程组件更改状态,需要分发一个action。分发在这里意味着将可执行信息发送到store。当一个store接收到一个action,它将把这个action代理给相关的reducer。reducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态。

3、使用redux完成计数器的案例

计数器对应的状态就是当前的数字,行为就是增加或者减少,我们使用redux来管理这个计数器案例。

  1. // redux的具体使用 计数器案例
  2. // 1、引入redux 在这里需要使用require()函数来引入 在脚手架中可直接使用commonjs的语法
  3. const redux = require('redux')
  4. // 2、定义redux需要管理的状态
  5. const defaultStatus = {
  6. counter: 200
  7. }
  8. // 3、定义需要修改状态的行为action
  9. const action1 = { type: 'INCREMENT' }
  10. const action2 = { type: 'DECREMENT' }
  11. // 4、创建reducer 形成为(state,action)=> state的纯函数
  12. function reducer(state = defaultStatus, action) {
  13. switch (action.type) {
  14. case 'INCREMENT':
  15. return {...state, counter: state.counter + 1 }
  16. case 'DECREMENT':
  17. return {...state, counter: state.counter - 1 }
  18. default:
  19. return state
  20. }
  21. }
  22. // 5、创建redux的store来存放应用的状态api是 { subscribe, dispatch, getState }
  23. let store = redux.createStore(reducer)
  24. // 6、可以手动订阅更新 也可以事件绑定到视图层 事件订阅后 就会回调事件订阅的函数 回调函数就会自动执行
  25. store.subscribe(() => {
  26. const newState = store.getState().counter
  27. console.log('订阅获取最新的状态', newState)
  28. })
  29. // 7、改变内部state唯一方法是 dispatch一个action action可以被序列化 用于记录和储存下来 后期还可以以回放的形式执行
  30. store.dispatch(action1)
  31. store.dispatch(action2)

总结:使用redux管理状态的几个步骤 1、引入redux,引入的方式是不相同的 2、创建我们需要管理的初始化状态(一般为一个对象) 3、创建我们修改数据的行为action(一般为一个对象,里面定义修改数据的方法类型,修改的数据) 4、创建reducer纯函数,将我们的状态与行为联系在一起,根据不同的行为,内部处理以后,返回新的状态(注意:不能修改原始的状态) 5、使用redux来创建全局的状态管理仓库store,将我们的纯函数reducer作为参数传入进去 6、订阅store仓库中state发生变化的事件,订阅后将获取store中最新的状态 7、使用store来派发修改状态的行为。

4、拆分redux、模块化开发

在项目中对redux进行相应的拆分,分为不同的模块,使不同的模块完成不同的功能。

image.png

4.1 actionCreators.js文件

作用:定义生成action对象的函数。

  1. // 定义修改数据的行为
  2. // 引入常量
  3. import {
  4. INCREMENT,
  5. DECREMENT,
  6. MUL
  7. } from './constants.js'
  8. // 数据增加 普通函数定义 不推荐
  9. function incrementAction(num) {
  10. return {
  11. type: INCREMENT,
  12. num
  13. }
  14. }
  15. // 数据减少 箭头函数定义 不推荐
  16. const decrementAction = (num) => {
  17. return {
  18. type: DECREMENT,
  19. num
  20. }
  21. }
  22. // 数据相乘 箭头函数定义 简写形式 推荐写法
  23. const mulAction = num => ({
  24. type: MUL,
  25. num
  26. })
  27. // 将定义的函数进行导出
  28. export { incrementAction, decrementAction, mulAction }

4.2 constants.js

作用:定义action的类型,进行导出,在实际的业务组件和reducer纯函数中使用。

  1. export const INCREMENT = 'INCREMENT'
  2. export const DECREMENT = 'DECREMENT'
  3. export const MUL = 'MUL'

4.3 reducer.js纯函数

作用:将我们需要管理的状态和需要派发的行为进行结合,根据派发行为的不同,返回不同的数据状态。

  1. // 1、需要定义redux管理的初始化状态
  2. // 2、引入修改状态的行为action
  3. // 引入action
  4. import {
  5. INCREMENT,
  6. DECREMENT,
  7. MUL
  8. } from './constants.js'
  9. // 定义初始化状态
  10. const defaultStatus = {
  11. counter: 100
  12. }
  13. // 定义纯函数reducer
  14. function reducer(state = defaultStatus, action) {
  15. switch(action.type) {
  16. case INCREMENT:
  17. return {...state, counter: state.counter + action.num }
  18. case DECREMENT:
  19. return {...state, counter: state.counter - action.num }
  20. case MUL:
  21. return {...state, counter: state.counter * action.num }
  22. default:
  23. return state
  24. }
  25. }
  26. // 将reducer函数默认导出
  27. export default reducer;

4.3 index.js文件-store数据仓库的入口文件

原理:引入redux,创建store数据仓库,管理传入的状态,将reducer纯函数作为参数传入,再将store进行导出,提供给我们实际的业务组件使用。

  1. // 在这里应该将store进行导出 提供给业务组件使用 在业务组件中只需要使用store进行dispatch派发action即可
  2. // 需要引入redux
  3. import redux from 'redux'
  4. // 引入纯函数reducer纯函数作为参数
  5. import reducer from './reducer.js'
  6. const store = redux.createStore(reducer)
  7. export default store;

4.4 业务组件使用store的步骤

方法:引入store, 引入actionCreators文件,订阅store事件,并且派发相应的行为。

  1. // 当作业务组件使用 直接使用store 调用store来派发行为
  2. // 引入store
  3. import store from "./store/index.js"
  4. // 导入入action
  5. import {
  6. incrementAction,
  7. decrementAction,
  8. mulAction
  9. } from "./store/actionCreators.js"
  10. // 订阅store状态发生变化的事件
  11. store.subscribe(() => {
  12. // 获取最新的状态值
  13. const newState = store.getState();
  14. console.log(newState);
  15. })
  16. // 派发修改状态的行为
  17. store.dispatch(incrementAction(200));
  18. store.dispatch(decrementAction(200));
  19. store.dispatch(mulAction(5));

4.5 在react组件中使用的步骤

步骤: 1、在react组件的生命周期中,componentDidMount中使用store进行数据的订阅。订阅完成后,一旦我们的数据发生了变化,我们的订阅事件就会发生回调,此时数据更新后,我们调用this.setState()函数,就会重新调用render方法,界面就会更新。 2、在dom元素挂载以后,监听页面的相关的事件或者在其它的时机使用store来派发行为。再次更新页面数据。

4.6 redux使用的流程图

image.png
需要注意的事项:
1、redux推荐我们全局只创建一个store对象,不需要创建多个。
4.7 在node中,由于低版本的node不支持ES6的导入(import)和导出(export)的功能,在node的13.X版本下,我们需要进行一些特殊的配置,让node支持我们平时习惯使用的ES Module的功能,对package.json文件进行具体的配置,见下图:
截图:
15 Redux的初体验 - 图4

5、redu在组件中的使用

5.1 计数器案例

原理:数据的原始状态保存在redux中,创建store状态仓库,向外暴露store,那么我们的组件就可以根据store来获取最新的状态。同时,在组件挂载完毕以后,组件可以订阅subscribe全局仓库store书记的变化,一旦数据发生变化,我们使用setState来修改状态,组件就会重新进行渲染。我们就可以获取到最新的状态。同时在组件中我们可以进行dom元素的监听,可以利用stote来派发相应事件,修改redux中的状态。

About组件

  1. import React, { PureComponent } from 'react'
  2. // 引入全局状态store
  3. import store from '../store'
  4. import { addAction, subAction } from '../store/actionCreators'
  5. export default class About extends PureComponent {
  6. constructor(props) {
  7. super(props)
  8. this.state = {
  9. // 组件的状态为 store全局仓库的状态
  10. counter: store.getState().counter
  11. }
  12. }
  13. componentDidMount() {
  14. this.unsubscribe = store.subscribe(() => {
  15. const newState = store.getState().counter
  16. this.setState({
  17. counter: newState
  18. })
  19. })
  20. }
  21. componentWillUnmount() {
  22. this.unsubscribe()
  23. }
  24. render() {
  25. return (
  26. <div>
  27. <h2>About组件</h2>
  28. <h2>{ this.state.counter }</h2>
  29. <button onClick={ () => this.btn1Click()}>增加增加</button>
  30. <button onClick={ () => this.btn2Click()}>降低降低</button>
  31. </div>
  32. )
  33. }
  34. btn1Click() {
  35. store.dispatch(addAction())
  36. }
  37. btn2Click() {
  38. store.dispatch(subAction())
  39. }
  40. }

组件Home

  1. import React, { PureComponent } from 'react'
  2. // 引用全局状态
  3. import store from '../store'
  4. import { addAction, subAction } from '../store/actionCreators'
  5. export default class Home extends PureComponent {
  6. constructor(props) {
  7. super(props)
  8. this.state = {
  9. // 将全局的状态分发到组件中 在组件中可直接进行展示
  10. counter: store.getState().counter
  11. }
  12. }
  13. componentDidMount() {
  14. // 订阅事件 调用渲染函数 更新页面的数据
  15. this.unsubscribe = store.subscribe(() => {
  16. const newState = store.getState().counter
  17. // 修改页面 获取最新的状态数据
  18. this.setState({
  19. counter: newState
  20. })
  21. })
  22. }
  23. componentWillUnmount() {
  24. // 组件卸载的时候 取消订阅事件
  25. this.unsubscribe()
  26. }
  27. render() {
  28. return (
  29. <div>
  30. <h2>Home组件</h2>
  31. <h2>{ this.state.counter }</h2>
  32. <button onClick={ () => this.btn1Click() }>数据增加</button>
  33. <button onClick={ () => this.btn2Click() }>数据降低</button>
  34. </div>
  35. )
  36. }
  37. btn1Click() {
  38. // 派发事件
  39. store.dispatch(addAction())
  40. }
  41. btn2Click() {
  42. store.dispatch(subAction())
  43. }
  44. }

整体的业务流程:
image.png

5.2 计数器案例-自己封装的connect高阶函数

原理:在我们实际的业务组件中,组件A和组件B都有相同的逻辑,这个逻辑是,实际的业务组件先引入我们的状态仓库store,因为在这个仓库中我们可以获取我们最新的状态,我们可以监听、订阅我们状态发生变化的时机,订阅后数据发生变化的时候,我们可以更新页面数据,那么我们页面的数据就是最新的数据。在组件将要卸载的时候,我们可以取消订阅的事件。在DOM的事件中,我们事件的监听都有自己的回调函数,那么在监听后我们可以dispatch来派发action, 派发action, 就意味这可以修改相应的数据。一旦我们的数据发生了变化,订阅事件就会重新被激活,页面就会重新进行渲染,这就是内部的逻辑。 存在的问题:在上述的逻辑中,我们可以看出,我们实际的每个业务组件都需要相同的操作逻辑,获取store中的状态,将store中的状态保存为组件自身的状态,然后监听、订阅store发生的变化,然后修改组件自身的状态,更新页面,在组件卸载的时候,取消订阅,在操作DOM的时候,使用store来派发事件,修改store中的状态,页面重新进行渲染。所有的组件,都是这一套的逻辑,缺点是代码的重复率太高了,相同的代码有多个地方。而且整个流程操作比较复杂。 解决方案:抽象出一个高阶组件(实际上是一个函数),将我们业务组件需要做的事情,全部抽取到高阶组件中,业务组件实际需要完成的事情,都在高阶组件中完成,高阶组件将新的状态和修改状态的方法全部通过props传递到实际的业务组件中,我们的业务组件就可以直接使用。实际上,我们的第三方插件就是react-redux就是用来干这件事情的。

  1. import { PureComponent } from "react";
  2. import store from '../store'
  3. function connect(mapStateToProps, mapDispatchToProps) {
  4. return function enhanceHOC(WrappedComponent) {
  5. return class extends PureComponent {
  6. constructor(props) {
  7. super(props)
  8. this.staste = {
  9. storeState: mapStateToProps(store.getState())
  10. }
  11. }
  12. componentDidMount() {
  13. // 订阅store发生的变化并更新数据
  14. this.unsubscribe = store.subscribe(() => {
  15. this.setState({
  16. storeState: mapStateToProps(store.getState())
  17. })
  18. })
  19. }
  20. componentWillUnmount() {
  21. this.unsubscribe()
  22. }
  23. store
  24. render() {
  25. return <WrappedComponent {...this.props}
  26. {...mapStateToProps(store.getState())}
  27. {...mapDispatchToProps(store.dispatch)} />
  28. }
  29. }
  30. }
  31. }
  32. // 将connect函数进行导出
  33. export default connect;

在组件中使用,将公用的代码抽取出去了
About组件:

  1. import React, { PureComponent } from 'react'
  2. import connect from '../utils/connect'
  3. import {incrementAction, addNumberAction} from '../store/actionCreators'
  4. class About extends PureComponent {
  5. render() {
  6. return (
  7. <div>
  8. <h2>about组件</h2>
  9. <h2>当前技术:{this.props.counter}</h2>
  10. <button onClick={() => this.props.btn1Click() }>+1</button>
  11. <button onClick={() => this.props.btn2Click(5) }>+5</button>
  12. </div>
  13. )
  14. }
  15. }
  16. const mapStateToProps = state => {
  17. return {
  18. counter: state.counter
  19. }
  20. }
  21. const mapDispatchToProps = dispatch => {
  22. return {
  23. btn1Click() {
  24. dispatch(incrementAction())
  25. },
  26. btn2Click(num) {
  27. dispatch(addNumberAction(num))
  28. }
  29. }
  30. }
  31. export default connect(mapStateToProps, mapDispatchToProps)(About)

Home组件:

  1. import React, { PureComponent } from 'react'
  2. // 引入connect函数
  3. import connect from '../utils/connect'
  4. import {decrementAction, subNumberAction} from '../store/actionCreators'
  5. class About extends PureComponent {
  6. render() {
  7. return (
  8. <div>
  9. <h2>Home组件</h2>
  10. <h2>当前技术:{this.props.counter}</h2>
  11. <button onClick={() => this.props.increment() }>-1</button>
  12. <button onClick={() => this.props.addNumber(5) }>-5</button>
  13. </div>
  14. )
  15. }
  16. }
  17. const mapStateToProps = state => {
  18. return {
  19. counter: state.counter
  20. }
  21. }
  22. const mapDispatchToProps = dispatch => {
  23. return {
  24. increment() {
  25. dispatch(decrementAction())
  26. },
  27. addNumber(num) {
  28. dispatch(subNumberAction(num))
  29. }
  30. }
  31. }
  32. // 函数柯里化
  33. export default connect(mapStateToProps, mapDispatchToProps)(About)

5.3 计数器案例-抽取store(创建StoreContext)

上述代码的缺点:在我们自己封装的高阶函数中,还是存在对store的依赖,当把它看成是一个工具函数的时候,我们就不应该直接使用实际的业务数据,我们需要在使用的时候,应该从他的上一级组件进行传入,而不是直接对业务数据进行直接的引入。所以我们引入context函数。 context作用:创建一个context上下文对象,对使用store的组件进行包裹,然后通过组件标签的props属性来传递store,在实际的业务组件中就可以使用this.context来直接获取store的值,然后对这个store的值进行订阅,转发到实际的业务组件中去。这就是原理。

  1. import React from 'react';
  2. // 创建context上下文对象
  3. const StoreContext = React.createContext()
  4. export default StoreContext

5.4 计数器案例-修改context函数的用法

  1. import { PureComponent } from "react";
  2. // 这里就不需要了
  3. // import store from '../store'
  4. // 引入context
  5. import StoreContext from './context'
  6. # 在这个高阶函数中 就不需要使用store 而是通过context来接收值 我们会在组件的外面的包裹一层context的高阶函数 context作为生产者 为我们提供状态数据 那么我们的业务组件也就是我们的消费者直接通过thiscontext来获取最新的状态即可。
  7. function connect(mapStateToProps, mapDispatchToProps) {
  8. return function enhanceHOC(WrappedComponent) {
  9. class EnhanceComponent extends PureComponent {
  10. constructor(props, context) {
  11. super(props, context)
  12. this.staste = {
  13. storeState: mapStateToProps(context.getState())
  14. }
  15. }
  16. componentDidMount() {
  17. // 订阅store发生的变化并更新数据
  18. this.unsubscribe = this.context.subscribe(() => {
  19. this.setState({
  20. storeState: mapStateToProps(this.context.getState())
  21. })
  22. })
  23. }
  24. componentWillUnmount() {
  25. this.unsubscribe()
  26. }
  27. render() {
  28. return <WrappedComponent {...this.props}
  29. {...mapStateToProps(this.context.getState())}
  30. {...mapDispatchToProps(this.context.dispatch)} />
  31. }
  32. }
  33. # 接收context传递的参数
  34. EnhanceComponent.contextType = StoreContext
  35. // 将组件返回
  36. return EnhanceComponent
  37. }
  38. }
  39. // 将connect函数进行导出
  40. export default connect;

5.5 在组件中使用context

  1. import React, { PureComponent } from 'react'
  2. import About from './16_react-redux/pages/About'
  3. import Home from './16_react-redux/pages/Home'
  4. // 引入StoreContext
  5. import StoreContext from './16_react-redux/utils/context'
  6. // 引入store
  7. import store from './16_react-redux/store'
  8. export default class App extends PureComponent {
  9. render() {
  10. return (
  11. <div>
  12. {/* 为组件提供context */}
  13. <StoreContext.Provider value={store}>
  14. <About />
  15. <hr/>
  16. <Home />
  17. </StoreContext.Provider>
  18. </div>
  19. )
  20. }
  21. }
  22. # 注意 在这里使用的是StoreContext组件 传递的是一个value的值,那么在我们的子组件中,直接就可以使用this.context使用就可以 不需要使用 this.context.store 非常重要

6、使用react-redux来改造计数器案例

原理:redux代替我们封装了connect函数和context函数

  1. # 在父组件中进行使用 数据传递给子组件
  2. import React, { PureComponent } from 'react'
  3. import About from './16_react-redux/pages/About'
  4. import Home from './16_react-redux/pages/Home'
  5. // 引入StoreContext
  6. // import StoreContext from './16_react-redux/utils/context'
  7. // 引入store
  8. // import store from './16_react-redux/store'
  9. # 使用react-redux中的Provider 直接react-redux提供的高阶组件Provider 将组件映射为标签即可,将我们实际的业务组件放置于这个Provider高阶组件中即可
  10. import { Provider } from 'react-redux'
  11. import store from './16_react-redux/store'
  12. export default class App extends PureComponent {
  13. render() {
  14. return (
  15. <div>
  16. {/* 为组件提供context */}
  17. # 需要注意的是 我们在使用context的时候,在高阶组件中传值使用的是value属性,但是在react-redux中,他要求我们必须 使用store来传递数据 非常重要 不用这个属性的话 数据将无法进行传递
  18. <Provider store={store}>
  19. <About />
  20. <hr/>
  21. <Home />
  22. </Provider>
  23. </div>
  24. )
  25. }
  26. }

6.1 在具体的业务组件中进行使用

  1. import React, { PureComponent } from 'react'
  2. // import connect from '../utils/connect'
  3. // 引入react-redux中的connect函数 非常重要
  4. # import { connect } from 'react-redux'
  5. import {incrementAction, addNumberAction} from '../store/actionCreators'
  6. class About extends PureComponent {
  7. render() {
  8. return (
  9. <div>
  10. <h2>about组件</h2>
  11. <h2>当前技术:{this.props.counter}</h2>
  12. <button onClick={() => this.props.btn1Click() }>+1</button>
  13. <button onClick={() => this.props.btn2Click(5) }>+5</button>
  14. </div>
  15. )
  16. }
  17. }
  18. const mapStateToProps = state => {
  19. return {
  20. counter: state.counter
  21. }
  22. }
  23. const mapDispatchToProps = dispatch => {
  24. return {
  25. btn1Click() {
  26. dispatch(incrementAction())
  27. },
  28. btn2Click(num) {
  29. dispatch(addNumberAction(num))
  30. }
  31. }
  32. }
  33. // 使用react-redux中的contect函数
  34. export default connect(mapStateToProps, mapDispatchToProps)(About)

6.2 react-redux在react框架中使用总结

  1. // react-redux的使用原理总结:
  2. 1、安装react-redux第三方库
  3. yarn add react-redux
  4. 2、在具体的业务组件中直接使用connect函数
  5. import { connect } from 'react-redux'
  6. // 定义变量
  7. const mapStateToProps = {}
  8. const mapStateToDispatch = {}
  9. export default connect(mapStateToProps, mapStateToDispatch)(ComponentName)
  10. 3、在跟组件中向下传递store
  11. import store from 'store/index.js';
  12. 使用Provider
  13. import { Provider } from 'react-redux';
  14. 映射为相应的标签 相当于将store以全局变量的方式都传递给了所有的子组件
  15. <Provider store={store}>
  16. <App />
  17. <Provider>
  18. 4、在类组件中可以直接使用this.props的属性来访问全局传入的store对象
  19. 在函数式组件中直接使用props来接收跟组件传递过来的参数

7、组件中的异步操作

在之前简单的案例中,redux中保存的counter是一个本地定义的数据:

  • 我们可以直接通过同步的操作来dispatch action,state就会被立即更新。
  • 但是真实开发中,redux中保存的很多数据可能来自服务器,我们需要进行异步的请求,再将数据保存到redux中。

在之前学习网络请求的时候我们讲过,网络请求可以在class组件的componentDidMount中发送,所以我们可以有这样的结构:
image.png

7.1 在react的redux中发送异步的网络请求

  1. import React, { PureComponent } from 'react'
  2. // 引入react-redux中的connect函数
  3. import { connect } from 'react-redux'
  4. import {incrementAction, addNumberAction,changeBannerAction, changeRecommendAction} from '../store/actionCreators'
  5. // 在react中发送响应的网络请求
  6. // 引入axios
  7. import axios from 'axios'
  8. class About extends PureComponent {
  9. componentDidMount() {
  10. axios({url: 'http://123.207.32.32:8000/home/multidata'}).then(res => {
  11. console.log(res.data);
  12. // 轮播图列表
  13. const banner = res.data.data.banner.list
  14. // 推荐列表
  15. const recommend = res.data.data.recommend.list
  16. // 可以通过action派发事件
  17. this.props.changeBanner(banner)
  18. this.props.changeRecommend(recommend)
  19. })
  20. }
  21. render() {
  22. return (
  23. <div>
  24. <h2>about组件</h2>
  25. <h2>当前技术:{this.props.counter}</h2>
  26. <button onClick={() => this.props.btn1Click() }>+1</button>
  27. <button onClick={() => this.props.btn2Click(5) }>+5</button>
  28. </div>
  29. )
  30. }
  31. }
  32. const mapStateToProps = state => {
  33. return {
  34. counter: state.counter
  35. }
  36. }
  37. const mapDispatchToProps = dispatch => {
  38. return {
  39. btn1Click() {
  40. dispatch(incrementAction())
  41. },
  42. btn2Click(num) {
  43. dispatch(addNumberAction(num))
  44. },
  45. changeBanner(banner) {
  46. dispatch(changeBannerAction(banner))
  47. },
  48. changeRecommend(recommend) {
  49. dispatch(changeRecommendAction(recommend))
  50. }
  51. }
  52. }
  53. export default connect(mapStateToProps, mapDispatchToProps)(About)

7.2 actionCreateor.js文件

  1. import { INCREMENT, ADD_NUMBER, DECREMENT, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants'
  2. export const incrementAction = () => ({
  3. type: INCREMENT,
  4. })
  5. export const addNumberAction = num => ({
  6. type: ADD_NUMBER,
  7. num
  8. })
  9. export const decrementAction = () => ({
  10. type: DECREMENT
  11. })
  12. export const subNumberAction = num => ({
  13. type: SUB_NUMBER,
  14. num
  15. })
  16. // 修改banner的action
  17. export const changeBannerAction = banner => {
  18. return {
  19. type: CHANGE_BANNER,
  20. banner
  21. }
  22. }
  23. // 修改推荐数据的action
  24. export const changeRecommendAction = recommend => {
  25. return {
  26. type: CHANGE_RECOMMEND,
  27. recommend
  28. }
  29. }

7.3 constants.js常量文件

  1. const INCREMENT = 'INCREMENT';
  2. const ADD_NUMBER = 'ADD_NUMBER';
  3. const DECREMENT = 'DECREMENT';
  4. const SUB_NUMBER = 'SUB_NUMBER';
  5. // 修改banner和recommend
  6. const CHANGE_BANNER = 'CHANGE_BANNER';
  7. const CHANGE_RECOMMEND = 'CHANGE_RECOMMEND'
  8. export {
  9. INCREMENT,
  10. ADD_NUMBER,
  11. DECREMENT,
  12. SUB_NUMBER,
  13. CHANGE_BANNER,
  14. CHANGE_RECOMMEND
  15. }

7.4 reducer.js处理函数文件

  1. import { INCREMENT, ADD_NUMBER, DECREMENT, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants'
  2. const defaultState = {
  3. counter: 100,
  4. banner: [],
  5. recommend: []
  6. }
  7. function reducer(state = defaultState, action) {
  8. switch(action.type) {
  9. case INCREMENT:
  10. return {...state, counter: state.counter + 1 }
  11. case ADD_NUMBER:
  12. return {...state, counter: state.counter + action.num }
  13. case DECREMENT:
  14. return {...state, counter: state.counter - 1 }
  15. case SUB_NUMBER:
  16. return {...state, counter: state.counter - action.num }
  17. // 处理banner和recommend
  18. case CHANGE_BANNER:
  19. return {...state, banner: action.banner }
  20. case CHANGE_RECOMMEND:
  21. return {...state, recommend: action.recommend }
  22. default:
  23. return state
  24. }
  25. }
  26. export default reducer;

7.5 在其他的组件中使用react-redux中的数据

  1. import React, { PureComponent } from 'react'
  2. // 引入connect函数
  3. // import connect from '../utils/connect'
  4. import { connect } from 'react-redux'
  5. import {decrementAction, subNumberAction} from '../store/actionCreators'
  6. class About extends PureComponent {
  7. render() {
  8. return (
  9. <div>
  10. <h2>Home组件</h2>
  11. <h2>当前技术:{this.props.counter}</h2>
  12. <button onClick={() => this.props.increment() }>-1</button>
  13. <button onClick={() => this.props.addNumber(5) }>-5</button>
  14. <hr/>
  15. <h2>轮播图</h2>
  16. <ul>
  17. {
  18. this.props.banner.map(item => {
  19. return (
  20. <li key={item.acm}>
  21. <span>{ item.title }</span>
  22. <a href={item.image} style={{ marginLeft: '10px'}}>点击查看图片</a>
  23. </li>
  24. )
  25. })
  26. }
  27. </ul>
  28. <h2>推荐列表</h2>
  29. <ul>
  30. {
  31. this.props.recommend.map(item => <li key={item.acm}>{item.title}</li>)
  32. }
  33. </ul>
  34. </div>
  35. )
  36. }
  37. }
  38. const mapStateToProps = state => {
  39. return {
  40. counter: state.counter,
  41. # 非常重要
  42. banner: state.banner,
  43. recommend: state.recommend
  44. }
  45. }
  46. const mapDispatchToProps = dispatch => {
  47. return {
  48. increment() {
  49. dispatch(decrementAction())
  50. },
  51. addNumber(num) {
  52. dispatch(subNumberAction(num))
  53. }
  54. }
  55. }
  56. // 函数柯里化
  57. export default connect(mapStateToProps, mapDispatchToProps)(About)

7.6 在组件中使用react-redux的步骤总结

在组件中异步请求的数据如何交给我们的redux进行管理? 1、在类组件的生命周期函数componentDidMount() 中发送异步请求,并获取相应的异步数据。 2、将我们需要dispatch派发action的地方定义一个方法,这个方法将在组件中映射为mapStateToDispatch(),我们的组件获取了异步数据后,调用这个方法、派发相应的action。 3、当派发了相应的action后,就会自动调用我们的reducer方法,reducer函数集那个会根据我们action.type的类型对数据进行保存和相应的处理。此时我们的数据就实时的保存进了redux的store中了。 4、在其它的业务组件中如何使用store中的state,我们需要的数据在在mapStateToProps()中声明即可。那么此时我们的store中的状态就会映射为相应状态,只需要在组件中使用我们的props即可。