思路:
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;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)