react 配合使用redux,以及异步和接口配合使用相关知识,

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36407748/article/details/85218777

不要问你到底需不需要用这个redux,你直接用上就对了,不要怕
概念性的东西就不解释了,参考中文文档:https://www.redux.org.cn/,
那你可能会说了,我要是文档都看懂了,我还看博客干啥?的确是这样,这个文档有些地方确实难理解,它不跟vuex一样,看一遍就能理解个70%,再随便找些教程,就能开撸代码了,但关于这个redux,你有时候看好多博客说明,一上来就是先来个计数器例子,创建一个redux文件夹,在这个文件夹下创建几个文件,然后安装一堆依赖,可最后你看完后,一脸蒙蔽,这是啥?不是就用一个redux吗 ,怎么折腾了那么多东西,才能跑起来,哎!真难!,抱怨一圈,日子待过,代码还是待撸啊,我用例子解释以下redux是怎么一步步使用的,后边会用一个小例子说下redux怎么配合接口玩,不知道怎么配合接口还玩个啥子呦!

redux晦涩难懂,你多写几遍就懂了,因为它的写法很固定

Redux 核心概念 (个人理解)

Store    数据仓库,简单说,就是以前我们写在react组件中state中的数据 
Action 	 行为/动作,用它的地方是在调接口数据,或者用户的点击操作,需要使用			dispatch方法提交,
    		(dispatch(action))dispatch就是分发事件,不理解先放放	
Reduce	 一个纯函数,有多纯,我不知道,但我知道这个东西就是个函数,它的作用是给你返回一个新	
		 的state,来更新视图,接收两个参数(state,action)
Type 	 类型 一般会单独抽离出来,语法就是一个常量,这个type是你提交action时必须要带的	
		 东西,它会在reduce里来用这个type匹配给你返回什么样的state

单一数据流
在这里插入图片描述
**

计数器例子 点击加1 ,点击减1 ,先从最简单的说 这里只使用redux 配合react,慢慢说后边的 redux-react
还有中间件,以及配合调试工具是什么东西

**
希望你多写几个demo,只看关于它的教程,想快速上手而不自己写写还是没有啥用的

新建项目 npx create-react-app react-redux

在这里插入图片描述

vacode 打开项目,删除src下的所有目录,我们来自己新建,只留下index和App,一般开发中会单独做一个文件夹放redux的东西,让项目结构清晰,这个我们一步步拆分

index.js

import React from 'react';
import {render} from 'react-dom';
import App from "./App";
render(<App />, document.getElementById('root'));

App.js

import React, { Component } from 'react'

export class App extends Component {
    state = {
        count: 0
    }
    render() {
        const { count} = this.state
        return (
            <div>
                <h4>点击了{count}次</h4>

                <br />
                <select>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;
                <button>点击增加</button>&nbsp;
                <button>点击减少</button>&nbsp;
                <button>延时1秒增加</button>
            </div>
        )
    }
}
export default App;

运行 npm start (这里逻辑就是根据选择的数据,点击按钮操作后渲染视图)
在这里插入图片描述
项目目录
在这里插入图片描述

给按钮添加点击事件,获取选中的下拉框的值
改造App.js

import React, { Component } from 'react'

export class App extends Component {
    state = {
        count: 0
    }
    render() {
        const { count } = this.state
        return (
            <div>
                <h4>点击了{count}次</h4>
                <br />
                <select ref={select => this.select = select}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
            </div>
        )
    }
    // 点击增加
    increment = () => {
        // 获取选中的下拉框的值 这里乘1,是因为得到的值是String类型
        // let number = Number(this.select.value) 
        let number = this.select.value * 1
        // 加法计算
        let count = this.state.count + number;
        // 更新 state的值
        this.setState({ count })
    }
    // 点击减少
    decrement = () => {
        // 获取选中的下拉框的值
        let number = this.select.value * 1
        // 减计算
        let count = this.state.count - number;
        // 更新 state的值
        this.setState({ count })
    }
    // 异步操作延时加1
    incrementAsync = () => {
        setTimeout(() => {
            let number = this.select.value * 1
            // 加法计算
            let count = this.state.count + number;
            // 更新 state的值
            this.setState({ count })
        }, 1000)
    }
}

export default App

引入redux 处理计数器
npm i redux -S 或者yarn add redux -S
在这里插入图片描述
为了项目结构的清晰,修改src文件夹的目录,新建redux文件夹,这个文件下的自文件干什么的 ,看注释啊,不是有type --reducer–action–store嘛,先写type 和reducer
在这里插入图片描述

index.js

import React from 'react';
import { render } from 'react-dom';

import App from "./App";

/**
 *  引入 createStore方法 从redux ,用于创建 store 
 *  createStore()方法接收第一个参数是reducer
 **/

import { createStore } from 'redux'

// 引入reducer,因为当前只有一个函数,先采用解构方法引入

import { counter } from './redux/reducers'

// 将 reducer作为参数 传入 createStore()方法

const store = createStore(counter)

render(<App store={store}/>, document.getElementById('root'));

action-type.js

/**
 * 包含多个被提交的action的类型   常量  值为大写或者小写的字符串
 */


//  点击增加
export const INCUREMENT = "INCUREMENT";

// 点击减去  
export const DECUREMENT = "DECUREMENT";

// 延时增加
export const INCUREMENTASYNC = "INCUREMENTASYNC";

reducers.js

/**
 * 包含多个reduce 函数
 * reducer (Function): 接收两个参数,分别是当前的 state 和要处理的 action,返回新的state 。
 */

/**
 * 计数器,一个纯函数接收两个参数
 * @param {*} state     初始值,数据类型可以是引用也可以基本数据
 * @param {*} action    是个对象,必须有一个是type且必传,payload为参数,可选的  
 * 	你在action 里 提交的必须是这样的 {type:"type",payload:可选}
 */
export function counter(state = 0, action) {
    switch (action.type) {
        default:
            return state;
    }
}

此时刷新页面,没有报错,在index.js中打印 store 变量,会看到控制台打印
在这里插入图片描述
在这里插入图片描述
此时还没有使用redux来存储数据,还是使用的组件中的state,那下边接着改造,将App 组件中的state 的数据存储到redux 中,其实也就是在reducer中,还记得reducer接收两个参数吗,一个state,一个action
其中第一个state就是初始值,因为默认在app组件中点击了 0 次,所以这里就是初始值为0 ,下边接着改造,既然将组件中的state值放到了reducer中,那么App组件的state就不要了

App.js
在这里插入图片描述
还记得上边在index.js中打印的那个store变量不是有个getState吗,就是使用 this.props.store.getState() 来获取值,也就是原先使用this.state获取初始值的方式,都要换成this.props.store.getState() 这种方式,因为state没有了,此时this.setState()页可以删除了

import React, { Component } from 'react'
export class App extends Component {
    render() {
        const count = this.props.store.getState() 
        return (
            <div>
                <h4>点击了{count}次</h4>
                <br />
                <select ref={select => this.select = select}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
            </div>
        )
    }
    // 点击增加
    increment = () => {
        // 获取选中的下拉框的值 这里乘1,是因为得到的值是String类型
        // let number = Number(this.select.value) 
        let number = this.select.value * 1
        // 加法计算
        let count = this.props.store.getState() + number; 
        // 更新 state的值
        
    }
    // 点击减少
    decrement = () => {
        // 获取选中的下拉框的值
        let number = this.select.value * 1
        // 减计算
        let count = this.props.store.getState()  - number;
        // 更新 state的值
      
    }
    // 异步操作延时加1
    incrementAsync = () => {
        setTimeout(() => {
            let number = this.select.value * 1
            // 加法计算
            let count = this.props.store.getState()  + number;
            // 更新 state的值

        }, 1000)
    }
}

export default App

接着刷新页面,打开控制台,没有任何报错,但是你会发现此时的点击事件失效了,这是因为还有一个东西没有用,就是最后的action,操作页面出发点击事件,是一个行为吧,此时就需要你使用dispatch()方法分发这个事件到reducer里,然后reducer会给你返回一个新的state
在这里插入图片描述
接着改造
App.js
还记得控制台打印store的时候有个dispatch方法吗
此时在点击事件中改变 state的值就需要通过dispatch({type:类型,参数})
之所以传type 是因为在reducer里需要用type来匹配你这里分发的事件的type,根据这个type来决定返回什么样的state

this.props.store.dispatch({type:INCUREMENT,count})

来改变初始值,注意啊,action有同步的还有异步的,这里说的都是同步的,redux本身只能同步

import React, { Component } from 'react'
import { INCUREMENT,DECUREMENT } from "./redux/action-type";
export class App extends Component {
  render() {
    const count = this.props.store.getState()
    return (
      <div>
        <h4>点击了{count}次</h4>
        <br />
        <select ref={select => this.select = select}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
      </div>
    )
  }
  // 点击增加
  increment = () => {
    // 获取选中的下拉框的值 这里乘1,是因为得到的值是String类型
    let number = this.select.value * 1
    this.props.store.dispatch({type:INCUREMENT,number})
  }
  // 点击减少
  decrement = () => {
    // 获取选中的下拉框的值
    let number = this.select.value * 1
    // 更新 state的值
    this.props.store.dispatch({type:DECUREMENT,number})
  }
  // 异步操作延时加1
  incrementAsync = () => {
    let number = this.select.value * 1
    setTimeout(() => {
      this.props.store.dispatch({type:INCUREMENT,number})
    }, 1000)
  }
}

export default App

此时在 reducers.js中 打印 console.log(state,action) ,打开控制台会看到
在这里插入图片描述
接着修改reducer.js
因为action 传了type,所以在reducer里也需要用type来区分你的事件
你可以用switch,也可以用if - else,不管用什么,最后一定要return 一个新的state

import { INCUREMENT, DECUREMENT } from "./action-type";
export function counter(state = 0, action) {
  console.log(state,action)
  switch (action.type) {
    case INCUREMENT:
      return state + action.number;
    case DECUREMENT:
      return state - action.number;
    default:
      return state;
  }
}

好了,此时改造完,你又发现一个问题,视图没有更新,咋回事?打开控制台,你会发现,其实每次的值已经变化了,但是没有引起视图的更新,此时就需要用到监听了
在这里插入图片描述
在index.js最后加入

store.subscribe(() => render(
	<App store={store} />,
	 document.getElementById('root')
	)
)

subscribe()接收一个函数
此时刷新控制台,就可以看到视图更新了
好了,一个最简单的redux使用就完了,但是action 的这种写法,官方推荐单独抽离出来,接着重新改造,将action进行抽离,创建一个actions.js
actions.js

/**
 * 包含所有的action creator (是一个工厂函数)
 * 必须返回一个包含type属性的对象 
 */
import { INCUREMENT, DECUREMENT } from "./action-type";
// 增加的
// export const incrementCreate = function(member){
//   return {
//     type:INCUREMENT,
//     member:member
//   }
// }
// 增加的 es6 简写,返回引用数据类型时,必须是表达式的形式 否则无效
export const incrementCreate = (number) => ({ type: INCUREMENT, number })
// 减少的
export const decrementCreate = (number) => ({ type: DECUREMENT, number })

接着改造
App.js

import React, { Component } from 'react'
import { incrementCreate, decrementCreate } from "./redux/actions";
export class App extends Component {
  render() {
    const count = this.props.store.getState()
    return (
      <div>
        <h4>点击了{count}次</h4>
        <br />
        <select ref={select => this.select = select}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
      </div>
    )
  }
  // 点击增加
  increment = () => {
    let number = this.select.value * 1
    this.props.store.dispatch(incrementCreate(number))
  }
  // 点击减少
  decrement = () => {
    // 获取选中的下拉框的值
    let number = this.select.value * 1
    // 更新 state的值
    this.props.store.dispatch(decrementCreate(number))
  
  }
  // 异步操作延时加1
  incrementAsync = () => {
    let number = this.select.value * 1

    setTimeout(() => {
      this.props.store.dispatch(incrementCreate(number))
    }, 1000)
  }
}

export default App

既然action 进行抽离了,那么把store也单独拿出来

改造index.js

import React from 'react';
import { render } from 'react-dom';
import App from "./App";
import store from "./redux/store";
render(<App store={store} />, document.getElementById('root'));
store.subscribe(() => render(<App store={store} />, document.getElementById('root')))

redux文件夹下创建store.js
注意引入文件的路径

/**
 *  引入 createStore方法 从redux ,用于创建 store 
 *  createStore()方法接收第一个参数是reducer
 **/
import { createStore } from 'redux'
// 引入reducer,因为当前只有一个函数,先采用解构方法引入
import { counter } from './reducers'
// 将 reducer作为参数 传入 createStore()方法
const store = createStore(counter)
console.log(store)
export default store;

好了,刷新页面,没有任何问题啊,到这里就是redux单独配合react使用,虽然代码功能实现了,但是也有很多不好的地方,比如在App.js 中,使用了 this.props.store.dispatch() 这种redux的写法,而且代码耦合度很高,而且你会发现,掺杂了好多redux的代码,看的跟react也不搭啊,那么可不可以就只用react的语法就把redux使用了,答案是当然有,这就需要引入一个插件 react-redux,有了这个插件就不用再直接使用redux的语法了,当然,你不用这个插件也是可以的啊,有的教程一上来就是引入3个东西redux —react-redux-- react-thunk,也不说为什么,,其实你现在就应该明白了,只引入redux 也是可以的,react-redux插件帮你更优化了redux配合react使用

那好,看看react-redux的使用
https://react-redux.js.org/

yarn add react-redux -S
它提供了两个重要的东西,一个Provider组件,一个connect()方法

好了,安装完成,改造
index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from "./App";
import store from "./redux/store";
render((
  <Provider store={store}>
    <App />
  </Provider>
), document.getElementById('root'));

然后在修改组件,必须使用connect进行连接
App.js

import React, { Component } from 'react'
import { incrementCreate, decrementCreate } from "./redux/actions";
import PropTypes from 'prop-types'
/**
 * connect 连接 React 组件与 Redux store。 是一个函数,返回一个新的组件
 * 接收几个参数,最重要的是(state,第二个是对象)
 */
import { connect } from "react-redux";

export class App extends Component {
  // 验证所需要的数据的类型,  你想要就写,不想要就不写,这个不重要,重要的是最后的合并
  static propTypes = {
    count: PropTypes.number.isRequired,
    incrementCreate: PropTypes.func.isRequired,
    decrementCreate: PropTypes.func.isRequired,
  }

  render() {
    const {count} = this.props
    console.log(count);
    return (
      <div>
        <h4>点击了{count}次</h4>
        <br />
        <select ref={select => this.select = select}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
      </div>
    )
  }
  // 点击增加
  increment = () => {
    let number = this.select.value * 1
    // this.props.store.dispatch(incrementCreate(number))
    this.props.incrementCreate(number)
  }
  // 点击减少
  decrement = () => {
    // 获取选中的下拉框的值
    let number = this.select.value * 1
    // 更新 state的值
    this.props.decrementCreate(number)
    // this.props.store.dispatch(decrementCreate(number))
  
  }
  // 异步操作延时加1
  incrementAsync = () => {
    let number = this.select.value * 1

    setTimeout(() => {
      this.props.incrementCreate(number)
      // this.props.store.dispatch(incrementCreate(number))
    }, 1000)
  }
}
export default connect(
  state => ({count:state}),  // 这个必须是函数
  {incrementCreate,decrementCreate} // 这是个对象啊 
)(App) // 这样写以后导出的就不是App组件,而是被connect包装成了一个新的组件

刷新页面,没有任何问题,打开控制台,看到App组件被包装后了
在这里插入图片描述

这样搞以后,你就看到在App组件中就全部都是react组件的写法了,将redux的代码都交给react-redux组件管理了,而且也不用自己做监听了,你可以参考文档将这部分进行近一步优化,react-redux文档将组件分为了两部分,一个是UI组件,这部分组件没有redux的Api,另一个就是容器组件,具体的可以看看文档,算了,再写写吧!上边意思就是让你再抽离一下代码,那就再src下再建俩文件夹呗,一个是容器组件containers ,一个是UI组件conponents,将使用redux的Api的组件放到containers文件夹下,不使用redux的api的放到components下
在这里插入图片描述
index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import Count from "./containers/Count";
import store from "./redux/store";
render((
  <Provider store={store}>
    <Count />
  </Provider>
), document.getElementById('root'));
// store.subscribe(() => render(<App store={store} />, document.getElementById('root')))

App.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class App extends Component {
  // 验证所需要的数据的类型,  你想要就写,不想要就不写,这个不重要,重要的是最后的合并
  static propTypes = {
    count: PropTypes.number.isRequired,
    incrementCreate: PropTypes.func.isRequired,
    decrementCreate: PropTypes.func.isRequired,
  }
  render() {
    const { count } = this.props
    console.log(count);
    return (
      <div>
        <h4>点击了{count}次</h4>
        <br />
        <select ref={select => this.select = select}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
      </div>
    )
  }
  // 点击增加
  increment = () => {
    let number = this.select.value * 1
    // this.props.store.dispatch(incrementCreate(number))
    this.props.incrementCreate(number)
  }
  // 点击减少
  decrement = () => {
    // 获取选中的下拉框的值
    let number = this.select.value * 1
    // 更新 state的值
    this.props.decrementCreate(number)
    // this.props.store.dispatch(decrementCreate(number))

  }
  // 异步操作延时加1
  incrementAsync = () => {
    let number = this.select.value * 1

    setTimeout(() => {
      this.props.incrementCreate(number)
      // this.props.store.dispatch(incrementCreate(number))
    }, 1000)
  }
}

Count.js


import { incrementCreate, decrementCreate } from "./../redux/actions";

import { connect } from "react-redux";

import App from '../components/App'

export default connect(
  state => ({ count: state }),  // 这个必须是函数
  { incrementCreate, decrementCreate } // 这是个对象啊 
)(App) // 这样写以后导出的就不是App组件,而是被connect包装成

接着刷新,没有问题啊

接着说redux,它有一个问题,就是默认不能进行异步处理
,而在实际开发中又肯定会进行ajax请求,或者定时器这些东西

此时为了处理异步请求,就需要用到一个中间件redux-thunk

npm i redux-thunk -S
使用它,改造store.js使用redux提供的applyMiddleware方法,

store.js

/**
 *  引入 createStore方法 从redux ,用于创建 store 
 *  createStore()方法接收第一个参数是reducer
 **/

import { createStore, applyMiddleware } from 'redux'

// 引入异步组件
import thunk from 'redux-thunk'

// 引入reducer,因为当前只有一个函数,先采用解构方法引入

import { counter } from './reducers'

// 将 reducer作为参数 传入 createStore()方法

const store = createStore(
  counter,
  applyMiddleware(thunk) // 使用中间件
)

console.log(store)
export default store;

App.js
// 异步操作延时加1, 这段注释所在的代码改写了

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class App extends Component {
  // 验证所需要的数据的类型,  你想要就写,不想要就不写,这个不重要,重要的是最后的合并
  static propTypes = {
    count: PropTypes.number.isRequired,
    incrementCreate: PropTypes.func.isRequired,
    decrementCreate: PropTypes.func.isRequired,
    incrementCreatorAsync: PropTypes.func.isRequired,
  }
  render() {
    const { count } = this.props
    console.log(count);
    return (
      <div>
        <h4>点击了{count}次</h4>
        <br />
        <select ref={select => this.select = select}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
      </div>
    )
  }
  // 点击增加
  increment = () => {
    let number = this.select.value * 1
    // this.props.store.dispatch(incrementCreate(number))
    this.props.incrementCreate(number)
  }
  // 点击减少
  decrement = () => {
    // 获取选中的下拉框的值
    let number = this.select.value * 1
    // 这里要重写
    this.props.decrementCreate(number)
    // this.props.store.dispatch(decrementCreate(number))

  }
  // 异步操作延时加1
  incrementAsync = () => {
    let number = this.select.value * 1
    this.props.incrementCreatorAsync(number)
  }
}

Counter.js


/**
 * 包含所有的action creator (是一个工厂函数)
 * 必须返回一个包含type属性的对象
 *  
 */
import { INCUREMENT, DECUREMENT } from "./action-type";

// 增加的 es6 简写,返回引用数据类型时,必须时表达式的形式 否则无效
export const incrementCreate = (number) => ({ type: INCUREMENT, number })

// 减少的
export const decrementCreate = (number) => ({ type: DECUREMENT, number })

/**
 * Action 有异步的,还有同步的,异步action 一般再开发中都是调接口
 * 需要您手动dispatch分发出去,参数是一个同步的action ,很凑巧,这里已经有一个同步的增加的action了
 * ,如果没有的话,你需要手动先提前创建同步的tacion,但不用暴露出去,因为
 * 这个同步action是为你的异步action服务的
 * @param {*} number 
 */
// 异步的增加
export const incrementCreatorAsync = (number) => {
  return dispatch => {
    setTimeout(() => {
      dispatch(incrementCreate(number))
    }, 1000)
  }
}

接着刷新页面,没有任何问题!

调试redux数据管理
https://www.npmjs.com/package/redux-devtools-extension

npm i redux-devtools-extension

改造一个store.js

/**
 *  引入 createStore方法 从redux ,用于创建 store 
 *  createStore()方法接收第一个参数是reducer
 **/

import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension';
// 引入异步组件
import thunk from 'redux-thunk'

// 引入reducer,因为当前只有一个函数,先采用解构方法引入

import { counter } from './reducers'

// 将 reducer作为参数 传入 createStore()方法

const store = createStore(
  counter,
  composeWithDevTools(
    applyMiddleware(thunk)
  ) // 使用中间件
)
console.log(store)
export default store;

在这里插入图片描述
需要安装 调试插件

好了,基本完事了,说一下跟接口怎么配合
跟接口配合的地方就在actions.js中的异步action操作中,但记住一点,先搞同步的,再搞异步的。代码演示一下

假设在reducer.js中再模拟一个reducer,用来模拟获取接口数据
当有多个reducer时,redux提供了一个合并多个小的reducer为一个大的reducer,暴露出去
import { combineReducers } from ‘redux’

/**
 * 包含多个reduce 函数
 * reducer (Function): 接收两个参数,分别是当前的 state 和要处理的 action,返回新的state 。
 * 也就是根据老的state 和 action 产生新的state 
 */

/**
 * 计数器,一个纯函数接收两个参数
 * @param {*} state     初始值,数据类型可以是引用也可以基本数据
 * @param {*} action    是个对象,必须有一个是type且必传,payload为参数,可选的  
 * 	你在action 里 提交的必须是这样的 {type:"type",payload:可选}
 * 
 */

import { INCUREMENT, DECUREMENT, BALL } from "./action-type";

import { combineReducers } from 'redux'

let initNum = 5;

// state = 0 参数默认值
function counter(state = initNum , action) {
  // console.log(state, action)
  switch (action.type) {
    case INCUREMENT:
      return state + action.number;
    case DECUREMENT:
      return state - action.number;
    default:
      return state;
  }
}

// 模拟接口数据,初始化数据为空
let initArr = [] 
function getData(state = initArr , action) {
  switch (action.type) {
    case BALL:
      return action.arr;
    default:
      return state;
  }
}
export default combineReducers({
  counter,
  getData,
})

actions.js
首先在这里去调接口,然后把接口中拿到的数据分发给reducer,去改变初始值

/**
 * 包含所有的action creator (是一个工厂函数)
 * 必须返回一个包含type属性的对象
 *  
 */
import { INCUREMENT, DECUREMENT,BALL } from "./action-type";

// 增加的 es6 简写,返回引用数据类型时,必须时表达式的形式 否则无效
export const incrementCreate = (number) => ({ type: INCUREMENT, number })

// 减少的
export const decrementCreate = (number) => ({ type: DECUREMENT, number })

/**
 * Action 有异步的,还有同步的,异步action 一般再开发中都是调接口
 * 需要您手动dispatch分发出去,参数是一个同步的action ,很凑巧,这里已经有一个同步的增加的action了
 * ,如果没有的话,你需要手动先提前创建同步的tacion,但不用暴露出去,因为
 * 这个同步action是为你的异步action服务的
 * @param {*} number 
 */
// 异步的增加
export const incrementCreatorAsync = (number) => {
  return dispatch => {
    setTimeout(() => {
      dispatch(incrementCreate(number))
    }, 1000)
  }
}
// 先写 获取接口信息的同步的action
const getBall = (arr)=>({type:BALL,arr})

// 模拟异步调接口
export const basketAsync = () => {
  return dispatch => {
    // 模拟调接口,最后得到的数据
    const arr = [
      { name:'足球',des:'我不会玩' },
      { name:'羽毛球球',des:'真好打不懂啊' }
    ]
    setTimeout(() => {
      dispatch(getBall(arr))
    }, 2000)
  }
}

actions-type.js

/**
 * 包含多个被提交的action的类型   常量  值为大写或者小写的字符串
 */


//  点击增加
export const INCUREMENT = "INCUREMENT";

// 点击减去  
export const DECUREMENT = "DECUREMENT";

// 延时增加
export const INCUREMENTASYNC = "INCUREMENTASYNC";

export const BALL = "BALL";

Counter.js


import { incrementCreate, decrementCreate, incrementCreatorAsync, basketAsync } from "./../redux/actions";

import { connect } from "react-redux";

import App from '../components/App'

export default connect(
  state => ({ obj: state }),  // 这个必须是函数
  { incrementCreate, decrementCreate, incrementCreatorAsync, basketAsync } // 这是个对象啊 
)(App) // 这样写以后导出的就不是App组件,而是被connect包装成

App.js
这个还是在componentDidMount这个钩子离调接口,你可以多打印一下

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class App extends Component {
  // 验证所需要的数据的类型,  你想要就写,不想要就不写,这个不重要,重要的是最后的合并
  static propTypes = {
    obj:PropTypes.object.isRequired,
    incrementCreate: PropTypes.func.isRequired,
    decrementCreate: PropTypes.func.isRequired,
    incrementCreatorAsync: PropTypes.func.isRequired,
  }
  componentDidMount(){
    this.props.basketAsync();
    // console.log(this.props)
  }
  render() {
    const { counter,getData } = this.props.obj
    // console.log(getData);
    return (
      <div>
        <h4>点击了{counter}次</h4>
        <br />
        <select ref={select => this.select = select}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
                <button onClick={this.increment}>点击增加</button>&nbsp;
                <button onClick={this.decrement}>点击减少</button>&nbsp;
                <button onClick={this.incrementAsync}>延时1秒增加</button>
        <div>
          <h4>模拟接口数据</h4>
          {
            getData.map((k,v)=>{
              return (
                <li key={v}>{k.name}</li>
              )
            })
          }
        </div>
        
      </div>
    )
  }
  // 点击增加
  increment = () => {
    let number = this.select.value * 1

    this.props.incrementCreate(number)
  }
  // 点击减少
  decrement = () => {
    let number = this.select.value * 1
    this.props.decrementCreate(number)
  }

  incrementAsync = () => {
    let number = this.select.value * 1
    this.props.incrementCreatorAsync(number)
  }
}

刷新两秒后,显示模拟的接口数据
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_36407748/article/details/85218777