标签:
对于react来说使用redux来管理状态是个不错的方案,redux是个很好的数据管理框架。但是redux并不是专门给react定制的数据管理框架,所以用它来管理react的数据也只能是凑合着用。redux麻烦的数据操作让我很反感,所以我想弄出一套适合自己的数据管理方式。
思路如下:扩展react的基类Component,扩展的基类构造里监听组件里的数据。监听的列表其实就是一个完整的状态树,组件名与组件的状态数据 一 一对应关系(组件与数据之间清晰的关系),整个页面的操作我只需要维护这个状态树。对于共享数据(状态)的问题,我收集了一份数据与组件依赖关系列表(数据:[组件、组件]),更新数据时通过数据来查找需要更新的组件集合逐一setState。
具体实现如下:
1 import React,{Component} from "react"; 2 var setStateList={}; 3 export default class newComponent extends Component{ 4 constructor(props){ 5 super(props); 6 this.props.listener&&event.listener(this.constructor.name,this.props.listener); 7 setStateList[this.constructor.name]=setState.bind(this); //收集每个组件的setState方法 8 this.constructor.listener=event.listener; //提供监听接口,可以监听额外数据 9 this.constructor.dispatch=event.dispatch; //发布接口 10 this.constructor.getData=event.getData; 11 this.init&&this.init(); //组件初始化提供接口 12 setData.call(this); //更新属性 13 } 14 shouldComponentUpdate(nextProps, nextState){ 15 setData.call(this,nextProps,nextState); 16 return this.ComponentUpdate?this.ComponentUpdate(nextProps, nextState):true; 17 } 18 } 19 function setData(props=this.props,state=this.state){ 20 if(props.listener){ 21 // 22 var data=Object.assign(props.listener,(this.data||{}),state); 23 delete data.listener; 24 } 25 //提供给组件使用的数据,因为react的this.props很难维护,所以添加了新的属性this.toProp来代替this.props 26 this.data=data?data:Object.assign({},state); 27 } 28 function setState(data){ 29 var component=this.constructor.name, 30 props=event.getData(component); 31 if(isObject(data)){ 32 //更新状态 33 this.setState(props?Object.assign(props,data):data); 34 } 35 } 36 var event=(()=>{ 37 var listenerData={}, //组件数据列表 38 updateComponents={}; //依赖列表,以数据决定依赖 39 function listener(type,data){ //监听数据 40 if(typeof type!=="string"&&isObject(type)){ 41 data=type; 42 type=this.name; 43 } 44 if(isObject(data)){ 45 listenerData[type]=data; 46 updateComponents[data]?updateComponents[data].push(type):updateComponents[data]=[type]; 47 } 48 } 49 function getData(type,status){ 50 type=type||this.name; 51 if(status){ 52 return Object.assign({},listenerData[type]); //获取的组件数据 53 }else{ 54 return listenerData[type]; 55 } 56 } 57 function dispatch(data,status){ //更新状态并处理依赖,param监听一个方方法的参数,status=true开启更新依赖模式 58 var type=this.name; //组件名 59 var components=updateComponents[listenerData[type]]; //获取使用该数据该的组件 60 if(!isObject(data)&&data!=="function"){ 61 throw new Error("dispatch参数错误!"); 62 } 63 if(typeof data==="function"){ 64 data(this); 65 return false; 66 } 67 if(!components){ 68 listener(type,data); 69 components=[type]; 70 } 71 if(!status){ 72 setStateList[type](data); 73 return false; 74 } 75 //更新所有使用了该数据的组件 76 components.forEach((item)=>{ 77 setStateList[item](data); 78 }) 79 } 80 return { 81 listener:listener, 82 getData:getData, 83 dispatch:dispatch, 84 } 85 })(); 86 var isObject=(data)=>{ 87 return (Object.prototype.toString.call(data).indexOf("object")!=-1); 88 } 89 //包装actions 90 let bindActionCreators=(action)=>(...param)=>(component)=>{ 91 component.dispatch(action(event.getData(component.name),...param)); 92 } 93 export let bindAction=(action)=>{ 94 if(typeof action=="function"){ 95 return bindActionCreators(action); 96 } 97 if(isObject(action)){ 98 var newAction={}; 99 var keys=Object.keys(action); 100 for(var i=0;i<keys.length;i++){ 101 newAction[keys[i]]=bindActionCreators(action[keys[i]]); 102 } 103 } 104 return newAction; 105 }
例子:
action
1 export function cut(state,data){ 2 return { 3 type:"CUT", 4 content:(state.content-1)||0, 5 } 6 } 7 export function addNav(state){ 8 state.nav.push({ 9 name:"微坛02", 10 id:15, 11 children:[ 12 { 13 name:"管理", 14 id:16, 15 children:[ 16 { 17 name:"邦德", 18 id:17, 19 } 20 ] 21 }, 22 { 23 name:"管理", 24 id:18, 25 children:[ 26 { 27 name:"邦德", 28 id:19, 29 }, 30 { 31 name:"火种", 32 id:20, 33 } 34 ] 35 } 36 ] 37 }) 38 return state; 39 } 40 export function createNav(){ 41 return (component)=>{ 42 setTimeout((res)=>{ 43 component.dispatch({ 44 nav:[{ 45 name:"药联", 46 id:1, 47 children:[ 48 { 49 name:"海景", 50 id:2, 51 children:[ 52 { 53 name:"邦德", 54 id:3, 55 } 56 ] 57 } 58 ] 59 }, 60 { 61 name:"微坛", 62 id:4, 63 children:[ 64 { 65 name:"管理", 66 id:5, 67 children:[ 68 { 69 name:"邦德", 70 id:6, 71 } 72 ] 73 }, 74 { 75 name:"管理", 76 id:7, 77 children:[ 78 { 79 name:"邦德", 80 id:8, 81 }, 82 { 83 name:"火种", 84 id:9, 85 } 86 ] 87 } 88 ] 89 }] 90 }) 91 },1000) 92 } 93 }
1 import ReactDOM from "react-dom"; 2 import React from "react"; 3 import {Col,Button,Row,Menu, Icon }from "antd"; 4 import * as actions from "../actions/actions.js"; 5 import Component,{bindAction} from "../react-extend.js"; //改造后的父类组件(构造方法会默认的收集该组件的setState方法) 6 var newactions=bindAction(actions); 7 const SubMenu = Menu.SubMenu; 8 const MenuItemGroup = Menu.ItemGroup; 9 class Nav extends Component{ 10 init(){ 11 newactions.createNav()(Nav); 12 } 13 mapItem(){ 14 var keys=Math.random()*15+1; 15 var loop=(data,Node,key="")=>{ 16 return data&&data.map((item,index)=>{ 17 keys++; 18 if(Node&&item.children){ 19 return <Node key={keys} title="分组1">{loop(item.children,null,"setting:")}</Node> 20 } 21 else if(item.children){ 22 return <SubMenu key={"SubMenu"+keys} title={<span><Icon type="setting" />{item.name}</span>}> 23 {loop(item.children,MenuItemGroup)} 24 </SubMenu> 25 }else{ 26 return <Menu.Item key={key+keys}> 27 <Icon type="mail" />{item.name} 28 </Menu.Item> 29 } 30 }) 31 } 32 return loop(this.data.nav||[]) 33 } 34 render(){ 35 this.data.nav&&Other.dispatch(this.data.nav[0]) 36 return <div> 37 <Menu mode="horizontal"> 38 {this.mapItem()} 39 </Menu> 40 <Test listener={this.data} test="添加菜单"/> 41 </div> 42 } 43 } 44 class Test extends Component{ 45 init(){ 46 47 } 48 clickHanlder(){ 49 newactions.addNav()(Nav) 50 } 51 restHanlder(){ 52 newactions.createNav()(Nav); 53 } 54 render(){ 55 return <div> 56 <h1 onClick={this.clickHanlder.bind(this)}>{this.props.test}</h1> 57 <p onClick={this.restHanlder.bind(this)}>重置</p> 58 </div>; 59 } 60 } 61 class Other extends Component{ 62 render(){ 63 return <p>{this.data.name}</p> 64 } 65 } 66 ReactDOM.render( 67 <div> 68 <Nav /> 69 <Other /> 70 </div>, 71 document.getElementById("container") 72 )
槽点:改变了react的constructor与shouldComponentUpdate的使用方式:constructor=>init、shouldComponentUpdate=>ComponentUpdate、V层使用this.data输出动态数据。由于使用比较灵活,没有限制,没有好的使用规范可能会导致组件之间交叉数据过于复杂,难以维护!
标签:
原文地址:http://www.cnblogs.com/dudeyouth/p/5483365.html