1. redux 理解

1.1 学习文档

  1. 英文文档: https://redux.js.org/
  2. 中文文档: http://www.redux.org.cn/
  3. Github: https://github.com/reactjs/redux

1.2 redux 是什么

  1. redux 是一个专门用于做状态管理的 JS 库(不是 react 插件库)。
  2. 它可以用在 react, angular, vue 等项目中, 但基本与 react 配合使用。
  3. 作用: 集中式管理 react 应用中多个组件共享的状态。

1.3 什么情况下需要使用 redux

  1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
  2. 一个组件需要改变另一个组件的状态(通信)。
  3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

1.4 redux 工作流程

redux 工作流程

2. redux 的三个核心概念

2.1 action

2.1.1 同步 action

  1. 动作的对象

  2. 包含2个属性

    • type:标识属性, 值为字符串, 唯一, 必要属性
    • data:数据属性, 值类型任意, 可选属性
  3. 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }

2.1.2 异步 action

  1. action 是一个函数

    //同步action,就是指action的值为Object类型的一般对象
    export const createIncrementAction = data => ({type: INCREMENT, data})
    export const createDecrementAction = data => ({type: DECREMENT, data})
    
    //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
    export const createIncrementAsyncAction = (data, time) => {
      return (dispatch) => {
        setTimeout(() => {
          dispatch(createIncrementAction(data))
        }, time)
      }
    }
    

2.2 reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的 state 和 action, 产生新的 state 的纯函数

2.3 store

  1. 将 state、action、reducer 联系在一起的对象

  2. 如何得到此对象?

    1. import {createStore} from 'redux'
    2. import reducer from './reducers'
    3. const store = createStore(reducer)
  3. 此对象的功能?

    • getState(): 得到 state

    • dispatch(action): 分发 action, 触发 reducer 调用, 产生新的 state

    • subscribe(listener): 注册监听, 当产生了新的 state 时, 自动调用

3. redux 的核心 API

3.1 createstore()

作用:创建包含指定reducer的store对象

3.2 store 对象

  1. 作用: redux 库最核心的管理对象

  2. 它内部维护着:

    • state

    • reducer

  3. 核心方法:

    • getState()

    • dispatch(action)

    • subscribe(listener)

  4. 具体编码

    • store.getState()
    • store.dispatch({type:'INCREMENT', number})
    • store.subscribe(render)

3.3 applyMiddleware()

作用:应用上基于 redux 的中间件(插件库)

3.4 combineReducers()

作用:合并多个 reducer 函数

4. 使用 redux 编写应用

4.1 效果

redux

4.2 纯 react 版本

export default class Count extends Component {

  state = {count: 0}

  //加法
  increment = () => {
    const {value} = this.selectNumber
    const {count} = this.state
    this.setState({count: count + value * 1})
  }
  //减法
  decrement = () => {
    const {value} = this.selectNumber
    const {count} = this.state
    this.setState({count: count - value * 1})
  }
  //奇数再加
  incrementIfOdd = () => {
    const {value} = this.selectNumber
    const {count} = this.state
    if (count % 2 !== 0) {
      this.setState({count: count + value * 1})
    }
  }
  //异步加
  incrementAsync = () => {
    const {value} = this.selectNumber
    const {count} = this.state
    setTimeout(() => {
      this.setState({count: count + value * 1})
    }, 500)
  }

  render() {
    return (<div>
      <h1>当前求和为:{this.state.count}</h1>
      <select ref={c => this.selectNumber = c}>
        <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.incrementIfOdd}>当前求和为奇数再加</button>
      &nbsp;
      <button onClick={this.incrementAsync}>异步加</button>
      &nbsp;
    </div>)
  }
}

4.3 redux 精简版

4.3.1 例子

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'

ReactDOM.render(<App/>, document.getElementById('root'))

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

redux/store.js

/* 
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)

redux/count_reducer.js

/* 
	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log(preState);
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case 'increment': //如果是加
			return preState + data
		case 'decrement': //若果是减
			return preState - data
		default:
			return preState
	}
}

components/Counter/index.jsx

import React, { Component } from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'

export default class Count extends Component {

	state = {carName:'奔驰c63'}

	/* componentDidMount(){
		//检测redux中状态的变化,只要变化,就调用render
		store.subscribe(()=>{
			this.setState({})
		})
	} */

	//加法
	increment = ()=>{
		const {value} = this.selectNumber
		store.dispatch({type:'increment',data:value*1})
	}
	//减法
	decrement = ()=>{
		const {value} = this.selectNumber
		store.dispatch({type:'decrement',data:value*1})
	}
	//奇数再加
	incrementIfOdd = ()=>{
		const {value} = this.selectNumber
		const count = store.getState()
		if(count % 2 !== 0){
			store.dispatch({type:'increment',data:value*1})
		}
	}
	//异步加
	incrementAsync = ()=>{
		const {value} = this.selectNumber
		setTimeout(()=>{
			store.dispatch({type:'increment',data:value*1})
		},500)
	}

	render() {
		return (
			<div>
				<h1>当前求和为:{store.getState()}</h1>
				<select ref={c => this.selectNumber = c}>
					<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.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</div>
		)
	}
}

4.3.2 总结

  1. 去除 Count 组件自身的状态

    2. src 下建立:
       * redux 目录
         * store.js
         * count_reducer.js
    
  2. store.js:

    1. 引入 redux 中的 createStore 函数,创建一个 store
    2. createStore 调用时要传入一个为其服务的 reducer
    3. 记得暴露 store 对象
  3. count_reducer.js:

    • reducer 的本质是一个函数,接收:preState,action,返回加工后的状态
    • reducer有两个作用:初始化状态,加工状态
    • reducer 被第一次调用时,是 store 自动触发的,
      • 传递的 preState 是 undefined,
      • 传递的 action 是: {type:'@@REDUX/INIT_a.2.b.4}
  4. 在 index.js 中监测 store 中状态的改变,一旦发生改变重新渲染 <App/>

    • 备注:redux 只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

4.4 redux 完整版

新增文件:

  1. count_action.js 专门用于创建 action 对象

  2. constant.js 放置容易写错的 type 值

redux/constant.js

/*
	该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

redux/count_action.js

/*
	该文件专门为Count组件生成action对象
*/
import {INCREMENT, DECREMENT} from './constant'

export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})

redux/count_reducer.js

/*
	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT, DECREMENT} from './constant'

const initState = 0 //初始化状态
export default function countReducer(preState = initState, action) {
  // console.log(preState);
  //从action对象中获取:type、data
  const {type, data} = action
  //根据type决定如何加工数据
  switch (type) {
    case INCREMENT: //如果是加
      return preState + data
    case DECREMENT: //若果是减
      return preState - data
    default:
      return preState
  }
}

components/Count/index.jsx

import React, {Component} from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import {
  createIncrementAction, createDecrementAction
} from '../../redux/count_action'

export default class Count extends Component {

  state = {carName: '奔驰c63'}

  //加法
  increment = () => {
    const {value} = this.selectNumber
    store.dispatch(createIncrementAction(value * 1))
  }
  //减法
  decrement = () => {
    const {value} = this.selectNumber
    store.dispatch(createDecrementAction(value * 1))
  }
  //奇数再加
  incrementIfOdd = () => {
    const {value} = this.selectNumber
    const count = store.getState()
    if (count % 2 !== 0) {
      store.dispatch(createIncrementAction(value * 1))
    }
  }
  //异步加
  incrementAsync = () => {
    const {value} = this.selectNumber
    setTimeout(() => {
      store.dispatch(createIncrementAction(value * 1))
    }, 500)
  }

  render() {
    return (<div>
      <h1>当前求和为:{store.getState()}</h1>
      <select ref={c => this.selectNumber = c}>
        <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.incrementIfOdd}>当前求和为奇数再加</button>
      &nbsp;
      <button onClick={this.incrementAsync}>异步加</button>
      &nbsp;
    </div>)
  }
}

5. redux 异步编程

5.1 理解

  1. redux默认是不能进行异步处理的,
  2. 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)

5.2 异步中间件

使用 npm install --save redux-thunk

  1. 明确:延迟的动作不想交给组件自身,想交给 action

  2. 何时需要异步 action:想要对状态进行操作,但是具体的数据靠异步任务返回。

  3. 具体编码:

    1. yarn add redux-thunk,并配置在 store 中
    2. 创建 action 的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
    3. 异步任务有结果后,分发一个同步的 action 去真正操作数据。
  4. 备注:异步 action 不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步 action。

5.3 代码实现

redux/store.js

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore, applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer, applyMiddleware(thunk))

redux/count_action.js

import {INCREMENT, DECREMENT} from './constant'

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data))
    }, time)
  }
}

componets/Count/index.js

//异步加
incrementAsync = () => {
  const {value} = this.selectNumber
  // setTimeout(()=>{
  store.dispatch(createIncrementAsyncAction(value * 1, 500))
  // },500)
}

6. react-redux

6.1 理解

  1. 一个 react 插件库,官方推出的
  2. 专门用来简化 react 应用中使用 redux

react-redux模型图

6.2 组件分类

  1. UI组件
    • 只负责 UI 的呈现、交互,不带有任何业务逻辑,
    • 通过 props 接收数据(一般数据和函数)
    • 不使用任何 Redux 的 API
    • 一般保存在 components 文件夹下
  2. 容器组件
    • 负责管理数据和业务逻辑,将结果交给 UI 组件,不负责 UI 的呈现
    • 使用 Redux 的 API
    • 一般保存在 containers 文件夹下
    • 容器组件中的 store 是靠 props 传进去的,而不是在容器组件中直接引入

6.3 相关 API

6.3.1 Provider

让所有组件都可以得到 state 数据

<Provider store={store}>
  <App />
</Provider>

6.3.2 connect

import { connect } from 'react-redux'
connect(
  mapStateToprops,
  mapDispatchToProps
)(Counter)

用来创建一个容器组件

  • mapStateToProps: 映射状态,返回值是一个对象
  • mapDispatchToProps: 映射操作状态的方法,返回值是一个对象

6.3.3 mapStateToprops

将外部的数据(即 state 对象)转换为 UI 组件的标签属性

6.3.4 mapDispatchToProps

将分发 action 的函数转换为 UI 组件的标签属性。

mapDispatchToProps,也可以是一个对象。

6.4 求和案例

App.js

import React, {Component} from 'react'
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {
  render() {
    return (<div>
      {/* 给容器组件传递store */}
      <Count store={store}/>
    </div>)
  }
}

containers/Count/index.jsx

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
  createIncrementAction, createDecrementAction, createIncrementAsyncAction
} from '../../redux/count_action'

//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

/*
	1.mapStateToProps函数返回的是一个对象;
	2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
	3.mapStateToProps用于传递状态
*/
function mapStateToProps(state) {
  return {count: state}
}

/*
	1.mapDispatchToProps函数返回的是一个对象;
	2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
	3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch) {
  return {
    jia: number => dispatch(createIncrementAction(number)),
    jian: number => dispatch(createDecrementAction(number)),
    jiaAsync: (number, time) => dispatch(
        createIncrementAsyncAction(number, time)),
  }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

components/Count/index.jsx

import React, {Component} from 'react'

export default class Count extends Component {

  state = {carName: '奔驰c63'}

  //加法
  increment = () => {
    const {value} = this.selectNumber
    this.props.jia(value * 1)
  }
  //减法
  decrement = () => {
    const {value} = this.selectNumber
    this.props.jian(value * 1)
  }
  //奇数再加
  incrementIfOdd = () => {
    const {value} = this.selectNumber
    if (this.props.count % 2 !== 0) {
      this.props.jia(value * 1)
    }
  }
  //异步加
  incrementAsync = () => {
    const {value} = this.selectNumber
    this.props.jiaAsync(value * 1, 500)
  }

  render() {
    //console.log('UI组件接收到的props是',this.props);
    return (<div>
      <h1>当前求和为:{this.props.count}</h1>
      <select ref={c => this.selectNumber = c}>
        <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.incrementIfOdd}>当前求和为奇数再加</button>
      &nbsp;
      <button onClick={this.incrementAsync}>异步加</button>
      &nbsp;
    </div>)
  }
}

6.5 求和案例-优化

6.5.1 优化流程

  1. 容器组件和 UI 组件整合一个文件

  2. 无需自己给容器组件传递 store,给 <App/> 包裹一个 <Provider store={store}> 即可。

  3. 使用了 react-redux 后也不用再自己检测 redux 中状态的改变了,容器组件可以自动完成这个工作。

  4. mapDispatchToProps 也可以简单的写成一个对象

  5. .一个组件要和 redux 打交道要经过哪几步?

    1. 定义好 UI 组件—不暴露

    2. 引入 connect 生成一个容器组件,并暴露,写法如下:

      connect(
        state => ({key:value}), //映射状态
        {key:xxxxxAction} //映射操作状态的方法
      )(UI组件)
      
    3. 在 UI 组件中通过 this.props.xxxxxxx 读取和操作状态

6.5.2 代码实现

index.js

export default class App extends Component {
  render() {
    return (<div>
      <Count/>
    </div>)
  }
}

containers/Count/index.jsx

import React, {Component} from 'react'
//引入action
import {
  createIncrementAction, createDecrementAction, createIncrementAsyncAction
} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

//定义UI组件
class Count extends Component {

  state = {carName: '奔驰c63'}

  //加法
  increment = () => {
    const {value} = this.selectNumber
    this.props.jia(value * 1)
  }
  //减法
  decrement = () => {
    const {value} = this.selectNumber
    this.props.jian(value * 1)
  }
  //奇数再加
  incrementIfOdd = () => {
    const {value} = this.selectNumber
    if (this.props.count % 2 !== 0) {
      this.props.jia(value * 1)
    }
  }
  //异步加
  incrementAsync = () => {
    const {value} = this.selectNumber
    this.props.jiaAsync(value * 1, 500)
  }

  render() {
    //console.log('UI组件接收到的props是',this.props);
    return (<div>
      <h1>当前求和为:{this.props.count}</h1>
      <select ref={c => this.selectNumber = c}>
        <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.incrementIfOdd}>当前求和为奇数再加</button>
      &nbsp;
      <button onClick={this.incrementAsync}>异步加</button>
      &nbsp;
    </div>)
  }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(state => ({count: state}),

    //mapDispatchToProps的一般写法
    /* dispatch => ({
      jia:number => dispatch(createIncrementAction(number)),
      jian:number => dispatch(createDecrementAction(number)),
      jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
    }) */

    //mapDispatchToProps的简写
    {
      jia: createIncrementAction,
      jian: createDecrementAction,
      jiaAsync: createIncrementAsyncAction,
    })(Count)

6.6 求和案例-数据共享版(最终版)

数据共享版

6.6.1 流程

  1. 定义一个 Pserson 组件,和 Count 组件通过 redux 共享数据。

  2. 为 Person 组件编写:reducer、action,配置 constant 常量。

  3. 重点:Person 的 reducer 和 Count 的 Reducer 要使用 combineReducers 进行合并,合并后的总状态是一个对象!!!

  4. 交给 store 的是总 reducer,最后注意在组件中取出状态的时候,记得“取到位”。

6.6.2 代码实现

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'

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

App.js

import React, {Component} from 'react'
import Count from './containers/Count'
import Person from './containers/Person'

export default class App extends Component {
  render() {
    return (<div>
      <Count/>
      <hr/>
      <Person/>
    </div>)
  }
}

redux/constant.js

/*
	该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'

readux/store.js

/*
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore, applyMiddleware} from 'redux'
//引入汇总之后的reducer
import reducer from './reducers'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'

//暴露store
export default createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))

redux/actions/count.js

/*
	该文件专门为Count组件生成action对象
*/
import {INCREMENT, DECREMENT} from '../constant'

//同步action,就是指action的值为Object类型的一般对象
export const increment = data => ({type: INCREMENT, data})
export const decrement = data => ({type: DECREMENT, data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const incrementAsync = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment(data))
    }, time)
  }
}

redux/actions/person.js

import {ADD_PERSON} from '../constant'

//创建增加一个人的action动作对象
export const addPerson = personObj => ({type: ADD_PERSON, data: personObj})

redux/reducers/index.js

/*
	该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import {combineReducers} from 'redux'
//引入为Count组件服务的reducer
import count from './count'
//引入为Person组件服务的reducer
import persons from './person'

//汇总所有的reducer变为一个总的reducer
export default combineReducers({
  count, persons
})

redux/reducers/count.js

/*
	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT, DECREMENT} from '../constant'

const initState = 0 //初始化状态
export default function countReducer(preState = initState, action) {
  // console.log('countReducer@#@#@#');
  //从action对象中获取:type、data
  const {type, data} = action
  //根据type决定如何加工数据
  switch (type) {
    case INCREMENT: //如果是加
      return preState + data
    case DECREMENT: //若果是减
      return preState - data
    default:
      return preState
  }
}

redux/reducers/person.js

import {ADD_PERSON} from '../constant'

//初始化人的列表
const initState = [{id: '001', name: 'tom', age: 18}]

export default function personReducer(preState = initState, action) {
  // console.log('personReducer@#@#@#');
  const {type, data} = action
  switch (type) {
    case ADD_PERSON: //若是添加一个人
      //preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
      return [data, ...preState]
    default:
      return preState
  }
}

containers/Count/index.jsx

import React, {Component} from 'react'
//引入action
import {
  increment, decrement, incrementAsync
} from '../../redux/actions/count'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

//定义UI组件
class Count extends Component {

  state = {carName: '奔驰c63'}

  //加法
  increment = () => {
    const {value} = this.selectNumber
    this.props.increment(value * 1)
  }
  //减法
  decrement = () => {
    const {value} = this.selectNumber
    this.props.decrement(value * 1)
  }
  //奇数再加
  incrementIfOdd = () => {
    const {value} = this.selectNumber
    if (this.props.count % 2 !== 0) {
      this.props.increment(value * 1)
    }
  }
  //异步加
  incrementAsync = () => {
    const {value} = this.selectNumber
    this.props.incrementAsync(value * 1, 500)
  }

  render() {
    //console.log('UI组件接收到的props是',this.props);
    return (<div>
      <h2>我是Count组件,下方组件总人数为:{this.props.personCount}</h2>
      <h4>当前求和为:{this.props.count}</h4>
      <select ref={c => this.selectNumber = c}>
        <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.incrementIfOdd}>当前求和为奇数再加</button>
      &nbsp;
      <button onClick={this.incrementAsync}>异步加</button>
      &nbsp;
    </div>)
  }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(state => ({
  count: state.count, personCount: state.persons.length
}), {increment, decrement, incrementAsync})(Count)


containers/Person/index.jsx

import React, {Component} from 'react'
import {nanoid} from 'nanoid'
import {connect} from 'react-redux'
import {addPerson} from '../../redux/actions/person'

class Person extends Component {

  addPerson = () => {
    const name = this.nameNode.value
    const age = this.ageNode.value * 1
    const personObj = {id: nanoid(), name, age}
    this.props.addPerson(personObj)
    this.nameNode.value = ''
    this.ageNode.value = ''
  }

  render() {
    return (<div>
      <h2>我是Person组件,上方组件求和为{this.props.count}</h2>
      <input ref={c => this.nameNode = c} type="text"
             placeholder="输入名字"/>
      <input ref={c => this.ageNode = c} type="text"
             placeholder="输入年龄"/>
      <button onClick={this.addPerson}>添加</button>
      <ul>
        {this.props.persons.map((p) => {
          return <li key={p.id}>{p.name}--{p.age}</li>
        })}
      </ul>
    </div>)
  }
}

export default connect(state => ({
      persons: state.persons, count: state.count
    }),//映射状态
    {addPerson}//映射操作状态的方法
)(Person)

7. redux 调试工具

7.1 前置条件

  1. yarn add redux-devtools-extension

  2. store 中进行配置

    import {composeWithDevTools} from 'redux-devtools-extension'
    const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
    
  3. chrome 安装 Redux DevTools

7.2 使用教程

actions

我们发现所列出的 action 右侧有两个按钮

  • jump 可以跳到对应的 action
  • skip 可以跳过某个 action,使某个 action 无效

action

  • 右侧 action 可以查看 action
  • state 可以查看当前 action 运行完后,store 中的 state

dispatch

  • 可以手动分发 action 进行测试

8. 纯函数和高阶函数

8.1 纯函数

  1. 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
  2. 必须遵守以下一些约束
    1. 不得改写参数数据
    2. 不会产生任何副作用,例如网络请求,输入和输出设备
    3. 不能调用 Date.now() 或者 Math.random() 等不纯的方法
  3. redux 的 reducer 函数必须是一个纯函数

8.2 高阶函数

理解: 一类特别的函数

  1. 情况1: 参数是函数
  2. 情况2: 返回是函数

常见的高阶函数:

  1. 定时器设置函数

  2. 数组的 forEach()/map()/filter()/reduce()/find()/bind()

  3. promise

  4. react-redux 中的 connect 函数

作用: 能实现更加动态, 更加可扩展的功能