Redux是什么?
redux是一个状态管理器,是一个帮你管理状态的js库,它提供一套单向流通的流程来管理数据状态(同flux架构),它可以很好的帮助MVC项目管理状态。
Redux何时使用,是不是画蛇添足?
正如同redux开发者所说 “很多开发者在不需要redux的时候就直接使用redux来管理状态,这并不是最优选择”,因为使用redux增加了一些额外的工作,需要建立仓库(createStore),设定处理逻辑(reducer),特定方法修改(dispatch),订阅(监听)修改(subscribe),这些额外的操作在没有用到redux之前可能只需要一行代码就解决,例如给某个对象的属性赋值+1,这样为什么要用redux呢,好像是画蛇添足一般。
其实使用redux就好比一家公司给员工配厨师做饭吃一样,当你的员工只有3人,此时配一位厨师专门给3人服务,显然是不划算的,公司需要有额外的开销,还要提供场地,配备厨具食材,这些都需要消耗很多精力,总体看削弱了团队的能力。但是如果是一家1000人的公司,那么专门建立一个食堂来为1000人服务,就变得很有意义了,这样食堂解决1000人点外卖的问题,便宜健康,公司还能通过经营食堂用更少的成本为员工提供更多的实惠,这样来看强化了团队的能力。由此可见,简单的项目不必使用redux,当项目变得庞大复杂 ,例如react中出现众多的组件间相互传值,大家用回调函数和全局对象来处理时会大大增加耦合性最终变成一堆乱麻,这时使用redux就十分有必要,因为redux存在的意义就是建立一套规则帮你管理状态,让状态流转变得井然有序,下面是一个redux基本应用:
<script src="https://cdn.bootcss.com/redux/4.0.0/redux.js"></script>
console.log(Redux,"Redux")
//设立 reducer,状态处理逻辑函数
const reducer_change = function(state={name:"1"},action){
switch(action.type){
case "changeInfo":
state.sexStr = action.data.sex;
return state
break;
case "changeNum":
return state
break;
default:
return state;
break;
}
}
//传入已经编写好的处理函数,建立仓库store
const myStore = Redux.createStore(reducer_change);
//用建立的仓库实例,订阅监听状态state的改变
myStore.subscribe(()=>{
console.log("stateChange----传入props触发render!")
})
//编写一个动作任务action
var action = {type:"changeInfo",data:{sex:"男"}}
//让仓库分配动作任务action给对应处理逻辑,此方法会被仓库监听执行对应回调,通常驱动
myStore.dispatch(action)
//获取状态
console.log(myStore,myStore.getState(),"store")
Redux的限制
Redux的确有很多好处,但是它也有很严格的限制:
必须用对象和数组来描述应用state
必须用简单对象来描述系统的变化
必须用纯函数来描述处理变化的逻辑
这些限制同时也很有用——下面这些功能都在这些限制的帮助下构建的:
- 将state存在本地,从而能关闭页面后再打开时恢复state链接
- 服务器将state放在html中发送给客户端,从而在此state基础上打开页面链接
- 触发bug时将用户action序列化,并获取state快照,用以发送bug报告以便于开发重现bug链接
- 通过网络传递action对象以实现协作环境,而不用复杂的代码修改链接
- 实现撤销功能及action实现乐观更新(即页面ui先变,再根据服务器返回结果更改ui,出错的话回滚),而不用复杂的代码修改链接
- 在state记录内切换,当代码变动时依据action历史计算当前state链接
- 为开发工具提供完整的检查和控制功能,便于开发为自家程序构建自定义工具链接
- 提供重用了大部分业务逻辑的可替代UI 链接
上述文章得知redux只是单向数据流架构的一种实现,它并不高深复杂,整个redux包才2KB大小,自己实现一个简易版本的redux也非常容易,如下代码实现一个简单的redux:
//store.js
function createStore(reducer){
//新建状态
let state = null;
//新建监听数组
const listeners = [];
//定义监听回调(订阅),触发定义回调
const subscribe = (listener)=> listeners.push(listener);
//定义获取状态
const getStore = ()=>{return state};
//定义修改状态方法
const dispatch = (action)=>{
state = reducer(state,action);
listeners.map((listener)=>{
listener();
})
}
dispatch({})
return {subscribe,getStore,dispatch};
}
简单几行代码你就能创造自己的store,该仓库也接受一个reducer,并拥有存储,订阅,使用reducer处理数据的功能,是不是特别简单?
react-redux是什么?
react-redux是redux在react中的具体实现,是针对react组件化开发的改造。
我们知道react中组件间传递props,可以通过props属性来传递,子组件获取父组件属性可以通过父组件定义的闭包函数(闭包中包含对父属性与方法的引用)来访问,另外还可以通过向全局暴露属性的方式来相互访问,不过这些方式都与redux单向数据流的概念背道而驰,很显然它们增加了复杂度与耦合性,但是react还有一个context的概念,context是一个特殊属性,拥有context属性的组件,其子组件都能访问到context中所存储的对象,这样可以避免props逐级传递,再最新的react版本中具体是通过context对象暴露两个组件来实现,Provider保存context,Consumer内访问Context:
const {Provider,Consumer} = React.createContext("")
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return <Provider value={myStore}>
<Consumer>
{(store)=>{
<Box1 store={store}/>
}}
</Consumer>
</Provider>
}
}
有了context,我们可以结合redux中的store,将store中的属性传递到任何一个组件中展示使用,所有的组件都用store中的dispatch方式来修改状态,这样统一管理状态,实现单向数据流,如何结合呢?react-redux帮我们实现了一个connect方法,传入需要使用的属性,需要用到的dispatch方法,以及一个组件,返回一个含有redux特性外壳的新组件,这样新组件就拥有了redux特性,下面看自己实现一个简易的react-redux实现:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>react学习</title>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- <script src="https://cdn.bootcss.com/redux/4.0.0/redux.js"></script> -->
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const myStore = createStore(reducer)
const {Provider,Consumer} = React.createContext("")
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return <Provider value={myStore}>
<div>
<Box1/>
<Box2/>
<Btns/>
</div>
</Provider>
}
}
class Box1 extends React.Component {
constructor(props) {
super(props);
// var state = this._getStateByStore()
// this._subscribeFn();
}
render() {
return <div style={{background:this.props.bgColor||"#acacac"}}>
title:{this.props.title||"111"}
</div>
}
}
var mapStateToProps_box1 = (state)=>{
return {
bgColor:state.bgColor,
title:state.title,
}
}
Box1 = connect(mapStateToProps_box1)(Box1);
class Box2 extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div style={{background:this.props.bgColor||"#eaeaea"}}>
title:{this.props.title||"~~~"}
</div>
}
}
var mapStateToProps_box2 = (state)=>{
return {
bgColor:state.bgColor_111,
title:state.title_111,
}
}
Box2 = connect(mapStateToProps_box2)(Box2);
class Btns extends React.Component {
constructor(props) {
super(props);
}
handleColorFn(){
this.props.changeBoxFn({
bgColor:"red",
title:"新标题00000000",
})
}
handleColor1Fn(){
this.props.changeBoxFn({
bgColor:"blue",
title:"新标题11111111",
})
}
render() {
return <div style={{background:this.props.bgColor,marginTop:"10px",cursor:"pointer"}}>
<button onClick={this.handleColorFn.bind(this)}>red</button><br/><br/><br/>
<button onClick={this.handleColor1Fn.bind(this)}>blue</button>
</div>
}
}
var mapStateToProps_btns = (state)=>{
return {
bgColor:state.bgColor,
title:state.title,
}
}
var mapDispatchToProps_btns = (dispatch)=>{
return {
changeBoxFn:(state)=>{
dispatch({
type:`changeColor`,
bgColor:state.bgColor,
})
dispatch({
type:`changeTitle`,
title:state.title,
})
}
}
}
Btns = connect(mapStateToProps_btns,mapDispatchToProps_btns)(Btns);
let domContainer = document.querySelector('#root');
ReactDOM.render(<App/>,domContainer)
//将context传值方式与redux关联起来,并抽离成为一个外壳,形成一个高阶组件(普通函数传入组件返回改造后的组件)
function getAllProps(mapStateToProps,mapDispatchToProps,store){
var obj = {};
obj={
...(mapStateToProps?mapStateToProps(store.getStore()):{}),
...(mapDispatchToProps?mapDispatchToProps(store.dispatch):{})
};
return obj;
}
//模拟react-redux 实现一个connect方法,将react contenxt特性与props关联,实现单向数据流,驱动MVC架构运行
function connect(mapStateToProps,mapDispatchToProps){
return (Comp)=>{
class Comp_reBuild extends React.Component {
constructor(props) {
super(props);
var state = getAllProps(mapStateToProps,mapDispatchToProps,this.props.store);
this.state={
allProps:state
}
if(!this.isSubscribe){
this.isSubscribe = true;
this.props.store.subscribe(()=>{
this._updataProps()
})
}
}
isSubscribe = false;
_updataProps(){
var state = getAllProps(mapStateToProps,mapDispatchToProps,this.props.store);
this.setState({
allProps:state
})
}
render() {
// var stateProps = mapStateToProps?mapStateToProps(this.props.store.getStore()):{};
// var dispatchProps = mapDispatchToProps?mapDispatchToProps(this.props.store.dispatch):{};
return <Comp {...this.state.allProps}/>
}
}
class ConsumerWithComp extends React.Component{
constructor(props) {
super(props);
}
render() {
return <Consumer>
{(store)=>{
return <Comp_reBuild store={store}/>
}}
</Consumer>
}
}
return ConsumerWithComp
}
}
//store.js
function createStore(reducer){
//新建状态
let state = null;
//新建监听数组
const listeners = [];
//定义监听回调(订阅),触发定义回调
const subscribe = (listener)=> listeners.push(listener);
//定义获取状态
const getStore = ()=>{return state};
//定义修改状态方法
const dispatch = (action)=>{
state = reducer(state,action);
listeners.map((listener)=>{
listener();
})
}
dispatch({})
return {subscribe,getStore,dispatch};
}
//reducers.js 传入state和action传出浅拷贝的state
function reducer(state,action){
if(!state){
return {}
}
switch (action.type) {
case "changeColor":
return {...state,bgColor:action.bgColor}
break;
case "changeTitle":
return {...state,title:action.title}
break;
default:
return state
break;
}
}
</script>
</body>
</html>
网友评论