redux-thunk使用教程

1,078 阅读8分钟
从无到有一步一步创建一个react-redux、redux-thunk使用教程:本教程GitHub地址:

https://github.com/chunhuigao/react-redux-thunk

创建react工程

在电脑上找一个文件夹,在命令窗使用create-react-app 创建我的react工程;这一步应该会的
create-react-app study-redux
看到下面这个图证明我创建完毕

启动react服务

在创建任务完成后进入我的创建的react工程,然后npm start运行,接着在浏览器http://localhost:3000/我应用该可以看到下面图片的场景

安装redux

要使用redux我需要使用npm将redux包安装到我的react工程中
在react工程的命令窗输入
npm install redux
npm install react-redux
npm install redux-thunk
如果报错,请使用淘宝代理cnpm安装。等待安装完毕,从新npm start启动项目

我的工程目录

删除无用的代码

将app.js中html结构代码删除,代码如下:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        {/* <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header> */}
      </div>
    );
  }
}

export default App;

这段代码对我学习redux没什么用,我喜欢代码简洁一点,所以删除了

创建第一个组件

在工程目录src/component中创建一个名为hello的文件夹,在hello文件夹中创建一个名为hello.js的文件。
在hello.js写如下代码:
import React, { Component } from 'react'

class Hello extends Component {
  render() {
    return (
      <div>
         <h2>学习redux</h2>
      </div>
    )
  }
}
export default Hello
在app.js中引入hello这个组件
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Hello from './component/hello/hello.js'
class App extends Component {
  render() {
    return (
      <div className="App">
        <Hello />
      </div>
    );
  }
}

export default App;
在浏览器中我应该可以看到“学习redux”这句文案了。

添加第一个事件

给hello添加一个按钮,点击按钮可以改变“学习redux”这句文案;hello.js具体代码如下:
import React, { Component } from 'react'
import './hello.css'
 class Hello extends Component {
  constructor(props){
    super(props)
    this.state = {
      title:'学习redux'
    }
  }
  changeTitle(){
    let title = '学习redux' + Math.ceil(Math.random()*100)
    this.setState({
      title:title
    })
  }
  render() {
    return (
      <div>
        <h2>{this.state.title}</h2>
        <button className="btn" onClick={()=>{this.changeTitle()}}>点击我改变标题</button>
      </div>
    )
  }
}
export default Hello

开始使用redux

下面这个流程刚开始接触redux可能云里雾里。但要坚持,跟着我的操作,你一定会看到结果的

index.js引用一下内容

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import { Provider } from 'react-redux'  //新引入
import {createStore} from 'redux'   //新引入
import rootReducer from './rootReducer.js'  //新引入

const store = createStore(rootReducer)
ReactDOM.render(
<Provider store={store}>
    <App />
</Provider>
, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

redux和react-redux之前安装过,引用后会报错,原因是引入的rootReducer.js这个文件需要创建;

创建rootReducer

在src文件夹创建rootReducer.js文件代码如下:
文件中combineReducers将拆分的页面单独reducer合并为一个大的reducer
import {combineReducers} from 'redux'
import helloReducer from './component/hello/helloReducer.js'
const rootReducer = combineReducers({
    helloReducer
})
export default rootReducer
这里面引入了一个helloReducer.js,这里我在hello.js同级文件夹创建helloReducer.js

创建helloReducer.js

reducer里面定义页面初始数据,在根据action.type操作state;具体代码如下:
const initState = {
    name:'hello,redux'
}
export default (state=initState,action) => {
    switch (action.type) {
        case 'CHANGE_NAME':
            return {...state,name:action.text}
        default:
            return state;
    }
}
然后将reducer数据绑定到组件中,这里需要用到react-redux中的connect函数

数据绑定到组件

这部分是在hello.js中进行的;具体代码如下:
import React, { Component } from 'react'

//引用connect
import {connect} from 'react-redux'

import './hello.css'
 class Hello extends Component {
  constructor(props){
    super(props)
    this.state = {
      title:'学习redux'
    }
  }
  changeTitle(){
    let title = '学习redux' + Math.ceil(Math.random()*100)
    this.setState({
      title:title
    })
   
  }
  render() {
//此处打印this.props应该可以看到helloProp这个对象了,证明数据已经挂在到组件紫红
    console.log(this.props)
    let {prop} = this.props
    return (
      <div>
        <h2>{this.state.title}</h2>
        <h3>我是来自redux数据{prop.name}</h3>
        <button className="btn" onClick={()=>{this.changeTitle()}}>点击我改变标题</button>
      </div>
    )
  }
}


//mapStateToProps是connect一个参数, 作用到将store中的数据作为 props 绑定到组件上 
const mapStateToProps = (state, ownProps) => {
  return {
    helloProp: state.helloReducer//helloReducer是在rootReducer中的名
  }
}
//通过connect高阶函数绑定数据
export default connect(mapStateToProps)(Hello)
数据已经绑定到组件了,怎么操作改变数据呢?需要用到action了

创建helloAction.js

在hello.js同级不目录创建helloAction.js;action主要负责发出动作指令,定义所有事件行为的;具体代码如下:
const helloAction = {
    changeName:(text)=>({
        type:"CHANGE_NAME",
        text:text,
        mate:'用于改变helloReducer中name值'
    })
}
export default helloAction
文件创建好怎么用呢?数据可以绑定到组件,action也是可以绑定到组件的,使用redux中bindActionCreators,具体代码看hello.js

将action绑定到组件

在hello.js引入新创建的helloAction.js,哎,看代码吧
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
//引入helloAction
import helloAction from './helloAction.js'
import './hello.css'
 class Hello extends Component {
  constructor(props){
    super(props)
    this.state = {
      title:'学习redux'
    }
  }
  changeTitle(){
    let title = '学习redux' + Math.ceil(Math.random()*100)
    this.setState({
      title:title
    })
   
  }
  render() {
    //此处打印,应该可以看到actions
    console.log(this.props)
    let {prop} = this.props
    return (
      <div>
        <h2>{this.state.title}</h2>
        <h3>我是来自redux数据{prop.name}</h3>
        <button className="btn" onClick={()=>{this.changeTitle()}}>点击我改变标题</button>
      </div>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    prop: state.helloReducer
  }
}

//使用mapDispatchToProps绑定helloAction
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    actions:bindActionCreators(helloAction,dispatch)
  }
}
//将mapDispatchToProps作为参数给connect
export default connect(mapStateToProps,mapDispatchToProps)(Hello)
改写changeTitle事件,修改redux传过来的数据
changeTitle(){
    let title = '学习redux' + Math.ceil(Math.random()*100)
    // this.setState({
    //   title:title
    // })
    let {actions} = this.props
    actions.changeName(title)
   
  }
效果图
孤例不为证,这个是在hello.js操作helloReducer的数据,不需要redux这么麻烦也可以搞定。下面我使用redux操作一下其他组件修改HelloReducer中的数据

创建other组件

在component文件夹中创建other文件夹,在other文件夹创建other.js;具体代码如下图
在app.js引入other.js
import React, { Component } from 'react';
import Hello from './component/hello/hello.js'
import Other from './component/other/other.js'
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Hello />
        <Other />
      </div>
    );
  }
}

export default App;

other具体代码

import React, { Component } from 'react'
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import helloAction from '../hello/helloAction.js'
 class Other extends Component {
  changeName(){
    let {helloAction} = this.props;
    let text = 'other' + (Math.random() *100)
    helloAction.changeName(text)
  }
  render() {
    let {otherProp} = this.props
    console.log(this.props)
    return (
      <div>
        <h3>我是其他组件</h3>
        <button className="btn" onClick={()=>{this.changeName()}}>我是其他组件的按钮</button>
      </div>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
    return {
        otherProp: state.helloReducer
    }
}
const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        helloAction:bindActionCreators(helloAction,dispatch)
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(Other)
点击按钮,同样可以操作helloReducer.js的数据,mapStateToProps只能绑定一个数据源吗?如果other也有reducer怎么办呢?

组件上绑定多个数据

在other.js同级目录创建otherReducer.js具体代码如下:
const initState = {
    title:'我是otherReducer数据'
}

export default (state=initState,action) => {
    switch (action.type) {
        case "CHANGE_TITLE":
            return {...state,title:action.text}
        default:
            return state
    }
}
在rootReducer.js中引用otherReducer.js,rootReducer代码如下:
import {combineReducers} from 'redux'
import helloReducer from './component/hello/helloReducer.js'
import otherReducer from './component/other/otherReducer.js'
const rootReducer = combineReducers({
    helloReducer,
    otherReducer
})
export default rootReducer
在other.js的mapStateToProps中绑定reducer数据
const mapStateToProps = (state, ownProps) => {
    return {
        helloProp: state.helloReducer,
        otherProp: state.otherReducer
    }
}
在render打印this.props试试?
  render() {
    let {helloProp,otherProp} = this.props
    console.log(this.props)
    return (
      <div>
        <h3>我是其他组件</h3>
        <button className="btn" onClick={()=>{this.changeName()}}>我是其他组件的按钮</button>
        <div>{otherProp.title}</div>
      </div>
    )
  }
到此,可以使用redux了。但这写只能执行同步操作,异步操作需要要结合redux-thunk或者redux-saga了;

redux异步操作

也是写一个简单的例子
在index.js引入redux-thunk
//注释import {createStore} from 'redux' 
//新增
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'

//注释 const store = createStore(rootReducer)
//新增
const store = createStore(rootReducer,applyMiddleware(thunk))
在component文件夹下创建example文件夹,在example文件夹创建example.js、exampleAction.js、exampleReducer.js具体代码如下:
example.js代码:
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import exampleAction from './exampleAction.js'
class Example extends Component {
  asyncFn(){
    let {exampleAction} = this.props
    exampleAction.asyncThing()
    console.log(this.props)
  }
  render() {
    let {exampleProp} = this.props
    return (
      <div>
        <h3>异步操作</h3>
        <h4>{exampleProp.text}</h4>
        <button className='btn' onClick={()=>{this.asyncFn()}}>我要进行异步操作</button>
      </div>
    )
  }
}
const mapStateToProps = (state, ownProps) => {
  return {
    exampleProp: state.exampleReducer
  }
}
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    exampleAction:bindActionCreators(exampleAction,dispatch)
  }
}
export default connect(mapStateToProps,mapDispatchToProps)(Example)
exampleReducer.js代码:
const initState={
    text:'我是用来测试异步操作的',
    sign:null
}
export default (state=initState,action) =>{
    console.log(action)
    switch (action.type) {
        case 'SUCCESS':
            return {...state,text:action.text,sign:'success'}
        case 'ERROR':
            return {...state,text:action.text,sign:'error'}
        default:
            return state
    }
}
exampleAction.js
 const exampleAction = {
    asyncThing:()=>{
        return (dispatch, getState) =>{
            // fetch('https://hacker-news.firebaseio.com/v0/jobstories.json')
            //     .then(res => {
            //         dispatch(exampleAction.asyncSuccess('我是成功回调'))  
            //     }).catch(e => {
            //         dispatch(exampleAction.asyncError('我是成功回调'))  
            // });
            setTimeout(() => {
                let sign = Math.random() >= 0.5 ? true : false;
                console.log(sign)
                sign ? dispatch(exampleAction.asyncSuccess('我是成功数据'))  : dispatch(exampleAction.asyncError('我是失败数据'))  
            }, 2000);
        }
    },
    asyncSuccess:(text)=>({
        type:'SUCCESS',
        text:text,
        mate:'异步成功操作'
    }),
    asyncError:(text)=>({
        type:'ERROR',
        text:text,
        mate:'异步成功操作'
    })
}
export default exampleAction
exampleReducer.js同样需要引入rootReduder.js中。
rootReducer代码:
import {combineReducers} from 'redux'
import helloReducer from './component/hello/helloReducer.js'
import otherReducer from './component/other/otherReducer.js'
import exampleReducer from './component/example/exampleReducer.js'
const rootReducer = combineReducers({
    helloReducer,
    otherReducer,
    exampleReducer
})
export default rootReducer
在app.js引入新建是example组件,应该可以看待如下界面
redux异步搞定了。

结束语:

不知我描述的清楚不清楚,希望这篇文章能不能帮到你。本人学识有限,如有文档中不规范或者误人子弟的错误言论,请指正。感谢你花时间阅读这篇文章,谢谢!