react中使用redux

-

 

 redux不是facebook团队开发的,不是只有react才可以用,angla、vue也可以用,不过vue中有专业的状态管理插件,vuex。vue3也出了一个pina。vue中并不用redux。

使用redux的原则是。能不用就不用,除非,不用比用更费劲。

react components : react组件

action creators:动作 创建者

dispatch: 派发

action: 是一个对象,包含 类型、数据

store: 存储

resucers: 加工厂

previous state: 之前的状态

new state:新状态

getState:获得状态

 

redux工作流程:

组件发出信息,要做些什么?传递给 动作创建者(action creator),动作创建者 创建好一个action对象(type:动作类型, data:执行动作需要的数据)派发给  老板(store),store 吩咐 reducer(加工者),加工者 把之前的状态 加工成新状态 返回给store(老板),组件向store中取状态。

用一个吃饭的例子举例:顾客(组件)对服务员说想吃饭,服务员列出一个单子:事件类型:吃饭,吃饭需要什么:蛋炒饭。给了老板(store),老板吩咐 厨师(reducer)做饭,厨师做完饭给老板,顾客从老板那里取饭。

 

求和案例纯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;
    setTimeout(() => {
      this.setState({ count: count + value * 1 })
    }, 1000);
  }
  render() {
    const { count } = this.state;
    return (
      <div>
        <h1>当前求和为: {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>
    )
  }
}

 redux api

store.getState(): 获取store中存储的值

store.dispatch(action): 派发动作,参数是一个动作对象{ type: 'xxx', data: xxx }
store.subscribe(() => {}): 监听store值的变化
// redux只维护状态,但是不会触发页面更新(不会触发组件render的调用)
// 检测redux中状态的变化,就调用render store.subscribe(() => { this.setState({}); // 传入空对象,只为触发组件的render方法 });

上面是在单个组件内监听store的状态变化,有一个狠办法,在index.js入口文件监听store,重新渲染app组件,就不用在每个组件都监听store了

不用担心效率问题,react有diff算法,如果组件没有变化,不会更新所有的组件的,不会引起页面重绘重排。

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
// 监听store中值的变化,更新App组件,就不用在每个组件中监听store掉用render了
store.subscribe(() => {
  root.render(<App />);
})

 

先写一个精简版的redux, action creator 先省略,用一个简单的对象来替代它。  store全局就有一个, 每个组件都有自己的 reducer;好,开干

redux相关的都放在redux文件夹下,创建store.js、count_reducer.js

redux/store.js:

/**
 * 该文件专门用于暴露一个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)
 */
// preState初始值是 undefined, 此时我们给他一个初始值
const initState = 0;
export default function countReducer(preState = initState, action) {
  // 从action对象中获取 type、data
  const { type, data } = action;
  switch(type) {
    case 'increment':  // 如果加
      return preState + data;
    case 'decrement': // 如果减
      return preState - data;
    default:
      return preState;
  }
}

Count组件使用redux:

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

export default class Count extends Component {

  // componentDidMount() {
  //   // 检测redux中状态的变化,就调用render
  //   store.subscribe(() => {
  //     this.setState({}); // 传入空对象,只为触发组件的render方法
  //   });
  // }

  // 加法
  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 })
    }, 1000);
  }

  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例子

上面的精简版的例子把actioncreator省去了,完整版的补回来,每个组件都有自己的actioncreator文件,专门为组件创建action对象。

在redux文件下给Count组件创建一个count_action.js文件:

/**
 * 改文件专门为Count组件生产action对象
 */

export const createIncrementAction = data => ({ type: 'increment', data });
export const createDecrementAction = data => ({ type: 'decrement', data });

为什么不把type也当做参数传过来呢?因为后面还有异步actioncreator

那么在Count组件中就不要直接用对象替代actioncreator的工作了,要使用actioncreator创建action对象:

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 {

  // componentDidMount() {
  //   // 检测redux中状态的变化,就调用render
  //   store.subscribe(() => {
  //     this.setState({}); // 传入空对象,只为触发组件的render方法
  //   });
  // }

  // 加法
  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))
    }, 1000);
  }

  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>
    )
  }
}

我们发现,action对象中的type在actioncreator中用到,在reducer中也用到了,那么最好把这个类型提取出来,防止某一个文件写错,导致程序无法正常工作。

在redux中创建一个constants.js文件,专门定义action中的type类型的常量值

constants.js:

/**
 * 改文件是专门定义action中type类型的常量值
 */
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';

将reducer和actioncreator中的type类型用constants中的常量替换:

count_reducer.js:

/**
 * 1.该文件是创建一个专门为Count组件服务的reducer,reducer的本质就是一个函数
 * 2.reducer函数会接到两个参数,分别是:之前的状态(preState)、动作对象(action)
 */
import { INCREMENT, DECREMENT } from './constants'
// preState初始值是 undefined, 此时我们给他一个初始值
const initState = 0;
export default function countReducer(preState = initState, action) {
  // 从action对象中获取 type、data
  const { type, data } = action;
  switch(type) {
    case INCREMENT:  // 如果加
      return preState + data;
    case DECREMENT: // 如果减
      return preState - data;
    default:
      return preState;
  }
}

count_action.js

/**
 * 改文件专门为Count组件生产action对象
 */
import { INCREMENT, DECREMENT } from './constants'

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

 redux异步action

返回值为函数的action就是异步action

下面把count组件的异步加用异步action写一下:

首先,异步action需要redux-thunk中间件的支持

安装redux-thunk

npm install redux-thunk

store.js

/**
 * 该文件专门用于暴露一个store对象
 */
// 引入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));

在count_action.js中写一个异步action

/**
 * 改文件专门为Count组件生产action对象
 */
import { INCREMENT, DECREMENT } from './constants'

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

在Count组件中使用:

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

export default class Count extends Component {

  // componentDidMount() {
  //   // 检测redux中状态的变化,就调用render
  //   store.subscribe(() => {
  //     this.setState({}); // 传入空对象,只为触发组件的render方法
  //   });
  // }

  // 加法
  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(createIncrementAsyncAction(value * 1, 1000))
    // }, 1000);
  }

  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>
    )
  }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

posted @ 2022-10-11 23:24  古墩古墩  Views(905)  Comments(0Edit  收藏  举报