React Redux

一、redux理解

1.学习文档

(1)英文文档:https://redux.js.org/

(2)中文文档:https://www.redux.org.cn/

(3)Github:https://github.com/reactjs/redux

2.redux是什么

(1)redux是一个专门用于做状态管理的JS库(不是react插件库)

(2)它可以用在react、angular、vue等项目中,但基本与react配合使用

(3)作用:集中式管理react应用中多个组件共享的状态

3.什么情况下需要使用redux

(1)某个组件的状态,需要让其他组件可以随时拿到(共享)

(2)一个组件需要改变另一个组件的状态(通信)

(3)总体原则:能不用就不用,如果不用比较吃力才考虑使用

4.redux原理图

二、redux的三个核心概念

1.action

(1)动作的对象

(2)包含2个属性

        type:标识属性,值为字符串,唯一,必要属性

        data:数据属性,值类型任意,可选属性

(3)例子:{type:'ADD_STUDENT',data:{'name:'tom',age:18}}

2.reducer

(1)用于初始化状态,加工状态

(2)加工时,根据旧的state和action,产生新的state的纯函数。

3.store

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

(2)如何得到此对象?

        import {createStore} from 'redux'

        import reducer from './reducers'

        const store = createStore(reducer)

(3)此对象的功能

        getState(): 得到state

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

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

三、求和案例

1.纯react
import React, { Component } from '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
    if (count%2 !== 0) {
      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>
      </div>
    )
  }
}
2.精简版

(1)去除Count自身的状态

(2).src下建立:

       -src

           -redux

              -store.js

              -count_reducer.js

(3)store.js

      引入redux中的createStore函数,创建一个store

      createStore调用时传入一个为其服务的reducer

      记得暴露store对象

(4)count_reducer.js

      reducer的本质是一个函数,接收preState,action,返回加工后的状态

      reducer有两个作用:初始化状态,加工状态

      reducer被第一次调用时,是store自动触发的,传递的preState是undefined

(5)在index.js中检测store中状态的改变,一旦发生改变重新渲染<App/>

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

// index.js
// 引入React
import React from 'react';
// 引入ReactDOM
import ReactDOM from 'react-dom/client';
// 引入App组件
import App from './App';
import store from './redux/store'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
store.subscribe(() => {
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
})



// Components/Count/index.jsx
import React, { Component } from 'react'
// 引入store用于获取store中的状态
import store from '../../redux/store'
export default class Count extends Component {
  state = {
    count : 0
  }
  // 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
    const count = store.getState()
    if (count%2 !== 0) {
      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>
      </div>
    )
  }
}


// redux/count_reducer.js
/*
  1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质是一个函数
  2.reducer函数会接收到两个函数,分别为:之前的状态preState,动作对象action
*/
const initState = 0
export default function countReducer(preState = initState,action){
  // 从action对象中获取type,data
  const {type,data} =action
  // 根据type决定如何加工数据
  switch (type) {
    case 'increment': // 如果是加
      return preState + data
    case 'decrement': // 如果是减
      return preState - data
    default:
      return preState
  }
}


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

// 引入createStore,专门用于创建redux中最为核心的store对象
// 5.0.1,createStore弃用了该版本

import { legacy_createStore as createStore} from 'redux'
// import { createStore } from "redux"

// 引入为Count组件服务的reducer
import countReducer  from './count_reducer'
// 暴露store
export default createStore(countReducer)
3.完整版

(1) 新增两文件:

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

        constant.js: 放置由于容易写错action中的type

// Count.jsx
import React, { Component } from 'react'
// 引入store用于获取store中的状态
import store from '../../redux/store'
// 引入actionCrator,专门用于常见action对象
import {createIncrementAction,createDecrementAction} from '../../redux/count_action'
export default class Count extends Component {
  state = {
    count : 0
  }
  // componentDidMount() {
  //   // 检测redux中状态的变化,只要变化,就调用render
  //   store.subscribe(() => {
  //     this.setState({})
  //   })
  // }
  // 加法
  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
    const count = store.getState()
    if (count%2 !== 0) {
      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>
      </div>
    )
  }
}


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


// reducer/count_action.js
/* 
  该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from './constant'
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})

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

// 引入createStore,专门用于创建redux中最为核心的store对象
// 5.0.1,createStore弃用了该版本

import { legacy_createStore as createStore} from 'redux'
// import { createStore } from "redux"

// 引入为Count组件服务的reducer
import countReducer  from './count_reducer'
// 暴露store
export default createStore(countReducer)


// reducer/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){
  // 从action对象中获取type,data
  const {type,data} =action
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT: // 如果是加
      return preState + data
    case DECREMENT: // 如果是减
      return preState - data
    default:
      return preState
  }
}

4.异步action版

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

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

(3)具体编码:

    npm i redux-thunk,并配置在store中

    创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务返回

    异步任务有结果后,分发一个同步的action去真正操作数据

(4)备注:异步action不是必须要写的,完全可以自己等待异步任务的结果再去分发同步action

// src/Components/Count/index.jsx
import React, { Component } from 'react'
// 引入store用于获取store中的状态
import store from '../../redux/store'
// 引入actionCrator,专门用于常见action对象
import {createIncrementAction,createDecrementAction, createIncrementAsyncAction} from '../../redux/count_action'
export default class Count extends Component {
  state = {
    count : 0
  }
  // componentDidMount() {
  //   // 检测redux中状态的变化,只要变化,就调用render
  //   store.subscribe(() => {
  //     this.setState({})
  //   })
  // }
  // 加法
  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
    const count = store.getState()
    if (count%2 !== 0) {
      setTimeout(() => {
        store.dispatch(createIncrementAsyncAction(value*1, 500))
      }, 500)
    }
  }
  render() {
    return (
      <div>
        {/* 
        (1)明确:延迟的动作不想交给组件自身,想交给action
        (2)何时需要异步action,想要对状态进行操作,但是具体的数据靠异步任务返回
        (3)具体编码:
           npm i redux-thunk,并配置在store中
           创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务返回
           异步任务有结果后,分发一个同步的action去真正操作数据
        (4)备注:异步action不是必须要写的,完全可以自己等待异步任务的结果再去分发同步action
        */}
        <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>
      </div>
    )
  }
}



// src/redux/constant.js

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


// src/redux/count_action.js

/* 
  该文件专门为Count组件生成action对象
*/
// 同步action,就是指action的值为函数
import {INCREMENT,DECREMENT} from './constant'
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)
  }
}


// src/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){
  // 从action对象中获取type,data
  const {type,data} =action
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT: // 如果是加
      return preState + data
    case DECREMENT: // 如果是减
      return preState - data
    default:
      return preState
  }
}

// src/redux/store.js

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

// 引入createStore,专门用于创建redux中最为核心的store对象
// 5.0.1,createStore弃用了该版本

import { legacy_createStore as createStore, applyMiddleware} from 'redux'
// import { createStore } from "redux"

// 引入为Count组件服务的reducer
import countReducer  from './count_reducer'
// 引入redux-thunk,用于支持异步action
import {thunk} from 'redux-thunk'
// 暴露store
export default createStore(countReducer,applyMiddleware(thunk))

 四、react-redux

1.理解

2.react-redux的基本使用

(1)明确两个概念:

    UI组件:不能使用在redux的api,只负责页面的呈现,交互等

    容器组件:负责和redux通信,将结果交给UI组件

(2)如何创建一个容器组件----redux的connect函数

    connect(mapStateToProps,mapDispatchToProps)(UI组件)

    mapStateToProps:映射状态,返回值是一个对象

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

(3)备注:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入

代码示例:

// src/Components/Count/index.jsx
import React, { Component } from 'react'
export default class Count extends Component {
  state = {
    count : 0
  }
  // 加法
  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
    if (this.props.count % 2 !== 0){
      this.props.jiaAsync(value*1, 500)
    }
  }
  render() {
    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>
      </div>
    )
  }
}


// src/containers/Count.jsx
// 引入Count的UI组件
import Count from "../../components/Count";
// 引入connect用于连接UI组件与redux
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction, createIncrementAsyncAction } from "../../redux/count_action";
// mapStateToProps函数返回的是一个对象
// 返回的对象中的key就作为传递给UI组件的props的key,value就作为传递给UI组件props的value-状态
// mapStateToProps用于传递状态
function mapStateToProps (state) {
  return {count:state}
}

// mapDispatchToProps函数返回的是一个对象
// 返回的对象中的key就作为传递给UI组件的props的key,value就作为传递给UI组件props的value-操作状态的方法
// mapDispatchToProps用于传递操作状态的方法
function mapDispatchToProps (dispatch) {
  return {
    // 通知redux执行加法
    jia:number => dispatch(createIncrementAction(number)),
    jian:number => dispatch(createDecrementAction(number)),
    jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number,time))
  }
}

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


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



// src/redux/count_action.js
/* 
  该文件专门为Count组件生成action对象
*/
// 同步action,就是指action的值为函数
import {INCREMENT,DECREMENT} from './constant'
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)
  }
}



// src/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){
  // 从action对象中获取type,data
  const {type,data} =action
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT: // 如果是加
      return preState + data
    case DECREMENT: // 如果是减
      return preState - data
    default:
      return preState
  }
}


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

// 引入createStore,专门用于创建redux中最为核心的store对象
// 5.0.1,createStore弃用了该版本

import { legacy_createStore as createStore, applyMiddleware} from 'redux'
// import { createStore } from "redux"

// 引入为Count组件服务的reducer
import countReducer  from './count_reducer'
// 引入redux-thunk,用于支持异步action
import {thunk} from 'redux-thunk'
// 暴露store
export default createStore(countReducer,applyMiddleware(thunk))


// App.js
import React, { Component } from 'react'
import Count from './containers/Count'
// 引入store
import store from "./redux/store";
export default class App extends Component {
  render() {
    return (
      <div>
        <Count store={store}/>
      </div>
    )
  }
}



// index.js
// 引入React
import React from 'react';
// 引入ReactDOM
import ReactDOM from 'react-dom/client';
// 引入App组件
import App from './App';
import store from './redux/store'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
store.subscribe(() => {
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
})

文件目录:

3.react-redux中mapDispatchToProps优化

mapDispatchToProps也可以是一个对象

// src/Containers/Count.jsx
// 引入Count的UI组件
import Count from "../../components/Count";
// 引入connect用于连接UI组件与redux
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction, createIncrementAsyncAction } from "../../redux/count_action";


// 使用connect()()创建并暴露一个容器组件,
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)
 4.Provider的使用
// App.js
import React, { Component } from 'react'
import Count from './containers/Count'
export default class App extends Component {
  render() {
    return (
      <div>
        <Count/>
      </div>
    )
  }
}



// index.js
// 引入React
import React from 'react';
// 引入ReactDOM
import ReactDOM from 'react-dom/client';
// 引入App组件
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

5.整合UI组件和容器组件

将src/Component/Count/index.jsx中的组件中的内容移入到容器组件中,其余文件不变。

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

import React, { Component } from 'react'
class Count extends Component {
  state = {
    count : 0
  }
  // 加法
  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
    if (this.props.count % 2 !== 0){
      this.props.jiaAsync(value*1, 500)
    }
  }
  render() {
    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>
      </div>
    )
  }
}

// 使用connect()()创建并暴露一个容器组件,
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.react-redux从基本使用到优化完成总结

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

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

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

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

(5)一个组件要和redux“打交道”要经过哪几步

    定义好UI组件 --- 不暴露

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

    connect(

       state => {{key:value}},  映射状态

       {key:xxxxAction}  映射状态操作的方法

    )(UI组件)

(6)在UI组件中通过this.props.xxxx读取和操作状态

五、react-redux整体使用数据共享案例

1.Person(添加人)与Count(求和)数据共享

(1)定义一个Person组件,和Count组件通过redux共享数据

(2)为Person组件编写,reducer、action,配置constant常量

(3)重点:Person的reducer和Count的reducer要使用combineReducers进行合并

    合并后的总状态是一个对象

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

// App.js
import React, { Component } from 'react'
import Count from './containers/Count' // 引入的Count的容器组件
import Person from './containers/Person' // 引入的Person的容器组件
export default class App extends Component {
  render() {
    return (
      <div>
        <Count/>
        <hr/>
        <Person/>
      </div>
    )
  }
}


// index.js
// 引入React
import React from 'react';
// 引入ReactDOM
import ReactDOM from 'react-dom/client';
// 引入App组件
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    {/* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */}
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);


// src/containers/Count/index.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { increment, decrement, incrementAsync } from '../../redux/actions/count'
class Count extends Component {
  state = {
    count : 0
  }
  // 加法
  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
    if (this.props.count % 2 !== 0){
      this.props.incrementAsync(value*1, 500)
    }
  }
  render() {
    return (
      <div>
        <h2>我是Count组件,下方组件总人数{this.props.personNum}</h2>
        <h3>当前求和为:{this.props.count}</h3>
        <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>
      </div>
    )
  }
}

// 使用connect()()创建并暴露一个容器组件,
export default connect(
  state => ({
    count:state.count,
    personNum:state.persons.length}),
  // mapDispatchToProps一般写法
  // dispatch => ({
  //   jia:number => dispatch(createIncrementAction(number)),
  //   jian:number => dispatch(createDecrementAction(number)),
  //   jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number,time))
  // })

  // mapDispatchToProps的简写
  {
    increment,
    decrement,
    incrementAsync
  }
)(Count)



// src/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
    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)



// src/redux/actions/count.js
/* 
  该文件专门为Count组件生成action对象
*/
// 同步action,就是指action的值为函数
import {INCREMENT,DECREMENT} from '../constant'
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)
  }
}


// src/redux/actions/person.js

import { ADD_PERSON } from "../constant";

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


// src/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){
  // 从action对象中获取type,data
  const {type,data} =action
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT: // 如果是加
      return preState + data
    case DECREMENT: // 如果是减
      return preState - data
    default:
      return preState
  }
}


// src/redux/reducers/index.js

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

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


// src/redux/reducers/person.js
import { ADD_PERSON } from "../constant";

// 初始化人的状态
const initState = [
  {id: '001',name: 'tom', age:10}
]
export default function personReducer(preState = initState, action) {
  const {type, data} = action
  switch (type) {
    case ADD_PERSON: // 若添加一个人
      return [data, ...preState]
    default:
      return preState
  }
}



// src/redux/constant.js

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



// src/redux/store.js

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

// 引入createStore,专门用于创建redux中最为核心的store对象
// 5.0.1,createStore弃用了该版本

import { legacy_createStore as createStore, applyMiddleware, combineReducers} from 'redux'
// import { createStore } from "redux"
// 引入汇总之后的reducer
import reducer from './reducers'
// 引入redux-thunk,用于支持异步action
import {thunk} from 'redux-thunk'

// 暴露store
export default createStore(
  reducer,
  applyMiddleware(thunk))
2.纯函数

(1)一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)

(2)必须遵守一下一些约束

    不得改写参数数据

    不会产生任何副作用,例如网络请求,输入和输出设备

    不能调用Date.now()或者Math.random()等不纯的方法

(3)redux的reducer函数必须是一个纯函数

六、react-redux开发者工具的使用

(1)npm i redux-devtools-extension

(2)store中进行配置

    import {composeWithDevTools} from 'redux-devtools-extension'

    const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值