React中类组件Redux的使用

1,138 阅读4分钟

Redux

状态管理器Redux可以方便的组织和管理组件之间的数据。

redux.png

Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。

redux-store.png

  • Redux基本原则

    • 单一数据源;
    • state是只读的;
    • 使用纯函数来执行修改;
  • Redux核心API

    • createStore(reducer):创建数据仓库需要引入import {createStore} from "redux";
    • store.getState():用于获取store里面的数据;
    • store.dispatch(action):用于派发action,触发reducer,执行修改动作;
    • store.subscribe(componentMethods):订阅store的修改,只要store发生改变,组件中的回调函数就会被执行;

类组件Redux的使用

1.store的创建和使用

1.使用Redux需要安装该模块
npm i redux –save
2.创建reducer文件提供数据源(src/store/reducer.jsimport {createStore} from "redux";
const defaultState = {
    name: "框架",
    list: ['vue', 'react', 'angular']
}
const reducer = (state=defaultState,action) => {
     switch(action.type) {
        case "CHANGEUSERNAME": 
            state.username=action.value
            break;
        default: 
            break;
    }
    state=JSON.parse(JSON.stringify(state))
    return state;//必须返回处理后的state,state需要深拷贝
}

const store = createStore(reducer);
export default store;

reducer函数会自己调用一次,state就是使用默认值初始化,之后如果组件中使用dispatch方法就会再次运行reducer函数

2.组件内部获取和使用数据:getState()

//组件内部引入仓库
import store from "./store/reducer.js";

//获取数据
store.getState().username

3.数据的修改和订阅

在组件内部定义要执行的动作action,并触发该动作dispatch。action中type字段是必须的,值通常大写。

// 数据的修改
changeValue() {
	const action = {
            type: "CHANGEUSERNAME",
            value: "测试数据"
    }
    store.dispatch(action);
}

组件中及时响应更新的数据:订阅。subscribe函数可以监听Redux中state的变化,一旦Redux中的state发生了变化,render函数就会被调用,页面就会被重新渲染

componentDidMount() {
    // 订阅store改变事件
    // this.sub方法会在reducer函数运行时调用,但是reducer函数第一次自动调用初始化时this.sub方法不会运行
	store.subscribe(this.sub.bind(this));
}
sub() {
    this.setState(store.getState())
}

仓库中接收动作并修改数据:通过dispatch触发动作后,会将action传递到reducer函数的第二个参数中,第一个参数state就是接受修改前上一次的状态。

const reducer = (state=defaultState,action) => {
     switch(action.type) {
        case "CHANGEUSERNAME": 
            state.username=action.value
            break;
        default: 
            break;
    }
    state=JSON.parse(JSON.stringify(state))
    return state;//必须返回处理后的state,state需要深拷贝
}

state进行深拷贝的原因是,当通过setState进行重新渲染时会将之前的storeState和现在获取到的newStoreState进行全等判断,如果store中只是修改了引用数据的某个属性,而引用地址没有改变,setState是不会重新渲染页面的。也保证了状态的唯一性。

案例使用

import React, { Component } from 'react'
import store from './store/reducer.js'
export default class App extends Component {
  componentDidMount(){
    // 获取数据
    this.setState({data:store.getState()});
  }
  render() {
    return (
      <div>
        <h1>App</h1>
        <p>{this.state.data.name}</p>
        {
          this.state.data.list.map((el,index)=>{
            return <p key={index}>{el}</p>
          })
        }
      </div>
    )
  }
}

image.png

componentDidMount生命周期函数在render函数后运行,此时this.state.data在render函数中还没有数据,再使用点语法取属性就会报错。

js中问号点 :

this.state?.data.name

作用就是判断这个对象this.state下的data是否为null或者undefined,当是null或者undefined时就直接返回undefined,这样属性取值也不会报错。

import React, { Component } from 'react'
import store from './store/reducer.js'
export default class App extends Component {
  componentDidMount(){
    // 获取数据
    this.setState({data:store.getState()});
  }
  render() {
    return (
      <div>
        <h1>App</h1>
        <p>{this.state?.data.name}</p>
        {
          this.state?.data.list.map((el,index)=>{
            return <p key={index}>{el}</p>
          })
        }
      </div>
    )
  }
}

image.png

import { createStore } from 'redux'

const defaultState = {
    name: "框架",
    list: ['vue', 'react', 'angular']
};

const reducer = (state = defaultState, action) => {
    // 该函数会自己调用一次,state就是使用默认值,之后是组件中使用dispatch方法也会再次调用
    console.log(state, action, 666);
    switch (action.type) {
        case "CHANGENAME":
            state.name=action.value;
            break;
        default:
            break;
    }
    state = JSON.parse(JSON.stringify(state))
    return state;//必须返回处理后的state,state需要深拷贝
};

const store = createStore(reducer);
export default store;
import React, { Component } from 'react'
import store from './store/reducer.js'
import First from './First.jsx';
export default class App extends Component {
  state={
    // 获取数据
    data: store.getState()
  }
  sub(){
    this.setState({ data: store.getState() });
  }
  componentDidMount() {
    // 订阅store改变事件,this.sub方法会在reducer函数运行时调用,但是reducer函数第一次自己调用时this.sub方法不会运行
    store.subscribe(this.sub.bind(this));
  }
  change() {
    const action = {
      type: 'CHANGENAME',
      value: '前端主流框架'
    }
    // store.dispatch方法调用会让仓库中reducer函数运行
    store.dispatch(action);
  }
  render() {
    return (
      <div>
        <h1>App</h1>
        <p>{this.state?.data.name}</p>
        <button onClick={this.change.bind(this)}>App组件修改仓库的数据</button>
        <First></First>
      </div>
    )
  }
}


import React, { Component } from 'react'
import store from './store/reducer.js'
export default class First extends Component {
    state = {
        // 获取数据
        info: store.getState()
    }
    componentDidMount(){
    // 订阅store改变事件
        store.subscribe(()=>{
            this.setState({ info: store.getState() })
        })
    }
    transmit=()=>{
    // 修改store中的数据
        store.dispatch({type: 'CHANGENAME',
        value: '前端三大主流框架'});
    }
    render() {
        return (
            <div>
                <h1>First</h1>
                <p>{this.state?.info.name}</p>
                {
                    this.state?.info.list.map((el, index) => {
                        return <p key={index}>{el}</p>
                    })
                }
                <button onClick={this.transmit}>First组件修改仓库数据</button>
            </div>
        )
    }
}

2.gif