Redux

Redux,第1张

方法一:自己封装的方法

思路:

1. 全局状态值在组件里都能访问到

  * context

  * 单独在内存中存放一个对象来做state的管理

2. 修改数据,

   用户通过dispatch 触发 数据修改函数, 返回新的数据赋值给state

3. 页面更新

   需要的组件通过subscribe 注册一个函数,保存到store对象的lister数组里

   数据改变的时候需要更新 事件分发

解构目录:

首先封装一个全局状态管理的文件store 里面有个index.js

在index.js文件夹下面

function createStore(reducer) {
  // 维护全局状态
  // state, listeners 希望是私有属性 外部不可访问
  let state = undefined;
  let listeners = [];
  // 获取全局状态值得方法
  let getState = () => {
    return state;
  };
  // 事件监听,当组件调用的时候就会把组件的方法存到listeners里面,在下面遍历执行
  let subscribe = (fun) => {
    listeners.push(fun);
  };
  // 触发state的修改
  const dispatch = (action) => {
    console.log("触发dispatch");
    // 修改值后返回结果
    state = reducer(state, action);//若没有改变state就是原始值
    // 数据修改完毕分发通知
    listeners.forEach(listener => listener())
  };

  // 初始化默认数据
  dispatch({ type: "", payload: "" });
  return {
    getState,
    subscribe,
    dispatch,
  };
}

const defaultState = {
  name: "韩梅梅",
  age: 123,

};
// 修改state值
function reducer(prevState = defaultState, action) {
  console.log("reducer执行了");
  console.log("action", action);
  console.log("prevState", prevState);
  let newData = prevState;
  // 对newData做修改
  const { type, payload } = action;
  switch (type) {
    // case "init":
    //   newData = defaultState;
    //   break;
    case "CHANGE_NAME":
      console.log("xxxxxxxx")
      newData.name = payload;
      break;

    default:
      break;
  }
  // 通过action 修改输入
  console.log("reducer数据", newData)
  return newData;
}

// 在任何地方只要能访问到对象就能访问到对象里的数据
const store = createStore(reducer);
export default store;

在son1 里面

import React, { Component } from "react";
import store from './store'
console.log("getState",store.getState())
class Son1 extends Component {
  constructor() {
    super();
  }
  state = {}

  render() {
    return (
    
      子组件1 {store.getState().name} //这个地方也是我们可以不用在调用store,直接在高阶组件里面把store里面的state,与dispatsh方法传递到每个组件的props里面,以后在调用的时候就不用再用store了
      
    );
  }

  componentDidMount() {  //在所有组件里面都需要调用store/index里面的subscribe方法所以我们想就可以把这个逻辑直接写在高阶组件里面
    store.subscribe(() => {
      console.log(store.getState())
      this.setState({})
    })
  }
}

export default Son1;

在son1组件使用后,我们发现每个组件都需要调用store里面的subscribe方法,并且每次需要从store里面获取state,与方法,我们想就可以封装一个高阶组件来直接从每个组件的props里面取值与方法

下面是我们自己封装的高阶组件间context.js

上代码


import React, { Component, Fragment } from "react";
import store from "./store";
export default (ContainConponet) => {
  class NewComponent extends Component {
    constructor() {
      super();
    }
    state = {} 
    render() {
      const result = store.getState();
      const fun  = store.dispatch;
      // 获取全局状态值映射到props里
      console.log(fun,'2222');
      return (
      
        
       {/*   */}{/* 注意在props传值得时候可以直接传递一个对象不用是一个自定义属性={} result是{name: '韩梅梅', age: 123}*/}
      );
    }
    componentDidMount() {
      // 监听
      store.subscribe(() => {
        this.setState({})
      })
    }
  } 
  return NewComponent
}

 在封装完高阶组件完之后,我们在son2.js里面试一下

import React, { Component } from "react";
import connect from "./connect"
//import store from "./store"  用了高阶组件之后就不用store了,可以直接在每个组建的props里面进行取值
@connect
class Son2 extends Component {
  constructor() {
    super();
  }
  state = {}

  render() {
    return (
    
      子组件2 {this.props.name}{/* 之前是store.getState().name */}
      
    );
  }
/* 下面的方法放到了高阶组件里面去了,所以不用在写 */
  // componentDidMount() {
  //   store.subscribe(() => {
  //     this.setState({})
  //   })
  // }
}

export default Son2;

接下来就是index.js父组件了,父组件没什么变化,最后再把父组件挂载到APP根组件就好了

import React, { Component } from "react";
import Son1 from "./Son1";
import Son2 from "./Son2";
class Demo extends Component {
  constructor() {
    super();
  }
  state = {}

  render() {
    return (
    
      自定义全局状态
      
      
      
    );
  }

  componentDidMount() {}
}

export default Demo;
方法二:通过插件redux

首先 npm  i  redux

首先看下目录解构

首先在index.js创建store 全局状态管理的代码,为了目录清晰,我们写了一个reducer.js进行导入到index.js里面

首先看index.js

import  {createStore} from "redux"; //我们首先使用插件redux里面的createStore方法
import reducer from "./reducer";  //把我们自己写的reducer 文件导入件来
const store = createStore(reducer); //把我们写的项目写入我们引进来的方法里,就相当于我们之前自己封装的createStore方法,用这个插件就不用我们自己写了,但是reducer还是需要我们自己去写
export default store; //最后把store导出就能用了

 在看下reducer.js,在这里面我们还要用到state.js里面是写默认state的值,actionTypes.js 是把所有的传进来的方法名写成一个常量,避免在大型项目的时候,把方法名字写错

我们先来看reducer.js

注意不能修改源数据

因为不能修改原数据,因为修改原数据改变的是对象的引用地址,所以直接修改原数据,引用地址没变,并不能实现更新,不更新就不能触发更新生命周期,也就不能在页面那种渲染出来

例如下面这样,直接修改newData里面的值

 const newData= prevState ;//这样写是不行的,需要进行深拷贝,下面有解决问题的方法

case "changename":
      newData.info.name = "李雷雷";
      return {
        ...prevState,
        info: {
          name: "李雷雷"
        }
      }
      break;

如果实在想改可以先在刚开始实现一下newdata的深拷贝,或者重新赋值一个引用地址,在我的博客immutable实现深浅拷贝_tianruine的博客-CSDN博客我们有更加高效的方法,immutable,或者用loash

const newData = JSON.parse(JSON.stringify(prevState)); 
export default (prevState = defaultState, action) => {
  // const newData= prevState ;//这样写是不行的
  //  不能修改原数据 引用类型修改原数据后 对比值得的改变是对比不出来的
  //  lodash
  const newData = JSON.parse(JSON.stringify(prevState));//方法一

  switch (type) 
    case "changename":
      newData.info.name = "李雷雷";
      // return {  //方法二
      //   ...prevState,
      //   info: {
      //     name: "李雷雷"
      //   }
      // }
      break;

    default:
      break;
  }
  
  return newData;
};

//可以发现与我们之前自己封装写的reducer函数一样
import DefaultState from "./state";
import { CHANGE_AGE, CHANGE_NAME } from "./actionTypes";
export default (prevState = DefaultState, action) => {
  const newData = { ...prevState };
  // 注意不能修改源数据
  const { type, payload } = action;
  switch (type) {
    case CHANGE_NAME:
      newData.name = payload;
      break;
    case CHANGE_AGE:
      newData.age = payload;
      break;
    default:
      break;
  }
  return newData;
};

state.js 默认的state

export default {
  name: "韩梅梅",
  age: 16
}

actionTypes.js  把方法进行写成常量,避免方法过多而写错或者写重复方法名字

const CHANGE_NAME = "CHANGE_NAME"
const CHANGE_AGE = "CHANGE_AGE"
export {
  CHANGE_NAME,
  CHANGE_AGE
}

以上我们就算把store写完了,但是我们还想在每个组件使用方法的时候,直接调用每个组件内部props里面的方法所以我们就写了一个actionCreatore.js,这是把每个组件所用使用的方法都放在这个文件夹里面

actionCreator 本质就是一个对象, 对象的目的是创建action

将action *** 作做统一的管理

下面是actionCreatore.js

import {CHANGE_AGE, CHANGE_NAME} from "./actionTypes"
export default {
  [CHANGE_NAME](payload) {
    const action = {
      type: CHANGE_NAME,
      payload
    }
    return action
  },
  [CHANGE_AGE](payload) {
    console.log("xxxx")
    const action = {
      type: CHANGE_AGE,
      payload
    }
    return action
  }
}

准备工作做完之后就是各个组件之间的代码了

首先先来看下几个插件

一个是react-redux,npm i react-redux,我们需要用到react-redux里面的connect方法

先看下connect

connect是一个函数

该函数接受2个参数 mapStateToProps, mapDispatchToProps

返回一个高阶组件

mapStateToProps 将state映射到props里 本质可以一个函数

1. 接受到全局状态store的state数据

2. 需要return 一个对象 将映射的对象映射到props里去

const mapStateToProps = (state) => {
  const {name, age } = state
  // console.log("xxxxx----arg",arg)
  return {
    name,
    age
  }
}
//所以可以写成state => state

mapDispatchToProps 将dispatch映射到props 本质也是也是一个函数

1. 如果改参数没有 会默认的将dispatch 映射到props中

2. 接受dispatch作为参数

3. 需要return对象, return的对象会放到props里去

const mapDispatchToProps = (dispatch) => {
  return {
    [CHANGE_AGE]:(age) => {
      const action = actionCreator[CHANGE_AGE](age)
      dispatch(action)
    },
  }
}  //每个函数都需要写成这样,所以我们可以使用bindActionCreators这个属性

简写就这样,会把store里面的方法全部都放到每个组件的props里面 dispatch => bindActionCreators(actionCreator,dispatch)
另一个是bindActionCreators,这个属性是react里面的

bindActionCreators帮助我们映射方法,并且自动的执行dispatch

现在我们来看下组件内部使用的方法

son1.js 

知识点:connect与bindActionCreators,通过connect高阶组件我们就可以拿到全局状态管理里面所有的props与所有方法,调用的时候直接this.props. 进行调用就行

import React, { Component } from "react";
import actionCreator from "./store/actionCreatore"
import { CHANGE_AGE, CHANGE_NAME} from "./store/actionTypes"
// import store from "./store"
import {connect} from "react-redux"
import { bindActionCreators } from "redux";
class Son1 extends Component {
  constructor() {
    super();
  }
  state = {}

  render() {
    return (
    
      组件1
      {/* {store.getState().name} */}
      {this.props.name} 
      {this.props.age} 
      
    );
  }
  componentDidMount() {
    console.log(this)
  }
}
export default connect(state => state, dispatch => bindActionCreators(actionCreator,dispatch))(Son1)

son2.js 我们进行对比,并没有用son1里面那连个插件,

还有个知识点,在组件销毁的时候,要进行对组件监听的事件进行销毁

store.subscribe 在注册一个监听之后 同时会返回一个取消监听的函数,所以在销毁阶段componentWillUnmount进行把返回值销毁

import React, { Component } from "react";
import store from "./store/index"
class Son2 extends Component {
  constructor() {
    super();
  }
  state = {}
  changeName = () => {
    store.dispatch({
      type:"CHANGE_NAME",
      payload: "隔壁老王"
    })
  }
  render() {
    return (
    
      组件2
      {store.getState().name}
      
年龄{store.getState().age} ); } componentDidMount() { // store.subscribe 在注册一个监听之后 同时会返回一个取消监听的函数 this.unsubscribe = store.subscribe(() => { this.setState({}) }) } componentWillUnmount() { this.unsubscribe && this.unsubscribe(); } } export default Son2;

在index.js 

在这里有需要用到一个插件Provider,也是react-redux

通过Provider 把store用属性穿进去,这样在每个组件里面才能使用connect高阶组件,每个组件才能通过props取到store里面的属性与方法

上代码,index.js

import React, { Component } from "react";
 import {Provider} from "react-redux" //用Provider属性
import Store from "./store";
import Son1 from "./Son1";
import Son2 from "./Son2";
class Demo extends Component {
  constructor() {
    super();
  }
  state = {
    show: true,
  };
  render() {
    return (
        //这里通过上下文进行传值
      
        redux demo
        {this.state.show && }
        
        
      
       
    );
  }
  componentDidMount() {}
}
export default Demo;
总结:

vuex

vue 一个类mvvm框架

react 视图层的js库 jq view

Flux redux 不止能在react 原生js也能用

* view 视图层

* actionCreator action 动作创建者

* dispatch 派发器

* store  存储应用状态

流程

1. 组件获取store里的状态应用在视图上(1.引入store 2.hoc)

2. 用户发起 *** 作,通过dispatch 派发action

3. actionCreator 接受到用户的 *** 作后 进行逻辑 *** 作,异步 *** 作

4. actionCreator 创建出action

5. 触发的修改数据方法里根据action修改数据

6. store 更新修改后的数据

7. 事件分发通知视图层修改

什么时候使用

redux vuex flux 像眼镜一样 需要的时候就自然就知道了

redux 设计思想

1. web应用的一个状态机 视图与状态一一对应

2. 所有的对象保存在一个状态里统一的数据源

3. 修改数据源的途径必须是统一的

https://www.redux.org.cn/

redux 基本使用

getState

subscribe unsubscribe

dipatch

actionCreator

actionTypes

优化:

1. 取值麻烦 store.getState().name

2. 改值也麻烦

3. 引入文件

react-redux

1. 通过provider提供器将store挂载到根组件的上下文

2. 通过connect将组件进行处理 简化了获取数据 和 监听的 *** 作

redux的异步方法1

我们在上一个的方法中的son1中通过有个setimeout来延时获取props里面的方法,再异步执行这个方法

上代码

import React, { Component } from "react";
import actionCreator from "./store/actionCreatore";
import { CHANGE_AGE, CHANGE_NAME } from "./store/actionTypes";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
class Son1 extends Component {
  constructor() {
    super();
  }
  state = {};
  getData = () => {  //就是定义一个自己的方法,延后延迟获取props里面的方法,就实现了异步
    setTimeout(() => {
      this.props[CHANGE_AGE](39);
    }, 3000);
  };
  render() {
    return (
      
        组件1
        {this.props.name}
        {this.props.age}
        
      
    );
  }
  componentDidMount() {
    console.log(this);
  }
}
export default connect(
  (state) => state,
  (dispatch) => bindActionCreators(actionCreator, dispatch)
)(Son1);
redux的异步方法2

处理异步问题的插件

redux-thunk

redux-saga

redux-effect

redux-promise

这里我们用的是redux-thunk,同事还要用到applyMiddleware中间件,是在redux插件里面的属性

代码:在store里面的index.js

import  {createStore, applyMiddleware} from "redux";
import reducer from "./reducer";
import thunk from "redux-thunk" //用到了redux-thunk
const store = createStore(reducer, applyMiddleware(thunk));//把thunk 放到中间件里面
export default store;

另一个改变就是actionCreatore.js里面方法的改动

import store from "./index";
import {CHANGE_AGE, CHANGE_NAME} from "./actionTypes"
export default {
  [CHANGE_NAME](payload) {
      const action = {
        type: CHANGE_NAME,
        payload
      }
      store.dispatch(action)
  },
  [CHANGE_AGE](payload) {
    setTimeout(() => {
      const action = {
        type: CHANGE_AGE,
        payload
      }
      store.dispatch(action) //这个地方不在是直接return action 而是通过store.dispatsh(action),这个地方是异步的处理这个函数
    },3000)
  }
}

在son1调用的时候

import React, { Component } from "react";
import actionCreator from "./store/actionCreatore"
import { CHANGE_AGE, CHANGE_NAME} from "./store/actionTypes"
import store from "./store"
class Son1 extends Component {
  constructor() {
    super();
  }
  state = {}

  render() {
    return (
    
      组件1
      {store.getState().name}
      
    );
  }

  componentDidMount() {
    // store.subscribe 在注册一个监听之后 同时会返回一个取消监听的函数
   this.unsubscribe =  store.subscribe(() => {
      this.setState({})
    })
  }
  componentWillUnmount() {
    this.unsubscribe &&  this.unsubscribe();
    console.log("组件销毁")
  }
}

export default Son1;
redux的模块化

首先来看下我们的结构

 我们在store全局管理里面创建了两个模块,分别是info,与list,这两个文件夹里面放的是默认state与对应处理方法的swich语句

例如在info文件下的reduce_info.js里面

import DefaultState from "./state";
import { CHANGE_AGE, CHANGE_NAME } from "../actionTypes";
export default (prevState = DefaultState, action) => {
  console.log("info_reducer")
  const newData = { ...prevState };
  // 注意不能修改源数据
  const { type, payload } = action;
  switch (type) {
    case CHANGE_NAME:
      newData.name = payload;
      break;
    case CHANGE_AGE:
      newData.age = payload;
      break;
    default:
      break;
  }
  return newData;
};

在list文件夹的reducer.js下

const defaultState = {
  list: [1, 2, 3, 4, 5, 6],
}; //默认数据

import { ADD_LIST } from "../actionTypes";
export default (prevState = defaultState, action) => {
  console.log("list-reducer");
  const newData = prevState;
  const { type, payload } = action;
  switch (type) {
    case ADD_LIST:
      
      const list = newData.list
      list.push(payload)
      newData.list = [...list]
      break;

    default:
      break;
  }
  return newData;
};

不同之处是在store的index.js文件夹里

需要用到combineReducers属性是redux的

import  {createStore, combineReducers} from "redux"; //引入combineReducers方法
import InfoReducer from "./info/reducer_info"
import ListReducer from "./list/reducer"
const reducer = combineReducers({ 在这里使用把两个模块合并起来
  info:InfoReducer,
  list:ListReducer
})
const store = createStore(reducer); //把两个都合并起来在放进到store里在导出去
export default store;

在son1.js使用的时候

import React, { Component } from "react";
import {connect} from "react-redux"
import actionCreatore from "./store/actionCreatore";
import { bindActionCreators } from "redux";
import { ADD_LIST } from "./store/actionTypes";
class Son1 extends Component {
  constructor() {
    super();
  }
  state = {};

  render() {
    return 
      组件1
      {this.props.name}
      {this.props.age}
      
    ;
  }

  componentDidMount() {
    console.log(this);
  }
}

// export default connect(state => state)(Son1);
//注意这个地方的state要用info里面的,要是不state.info,直接state=>state 拿到的是全部模块的,包括list,与info
export default connect(state => state.info,dispatch => bindActionCreators(actionCreatore,dispatch))(Son1);

看下son2.js 他就是直接用的state,没有用state.list模块,他拿的是所有模块

import React, { Component } from "react";
import {connect} from "react-redux"
import actionCreatore from "./store/actionCreatore";
import {bindActionCreators} from "redux"
import {CHANGE_NAME} from "./store/actionTypes" 
class Son2 extends Component {
  constructor() {
    super();
  }
  state = {};

  render() {
    return 
      组件2
     
       {(this.props.list.list || []).map((item,index)=> {//需要this.props.list模块下的list,因为下面用的全部模块的state
         return {item}
       })}
     
     
    ;
  }
  componentDidMount() {
    console.log(this)
  }
}
//这里直接拿的state全部模块,所以上面遍历的时候需要this.props.list模块下的list
export default connect(state => state, dispatch => bindActionCreators(actionCreatore,dispatch))(Son2);

父组件index.js 没什么改动

import React, { Component } from "react";
import {Provider} from "react-redux"
import Store from "./store";
import Son1 from "./Son1";
import Son2 from "./Son2";
class Demo extends Component {
  constructor() {
    super();
  }
  state = {
    show: true,
  };

  render() {
    return (
      
      
        模块化
        
        
      
      
    );
  }

  componentDidMount() {}
}

export default Demo;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1320238.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-11
下一篇 2022-06-11

发表评论

登录后才能评论

评论列表(0条)

保存