React JS 组件间沟通的一些方法

刚入门React可能会因为React的单向数据流的特性而遇到组件间沟通的麻烦,这篇文章主要就说一说如何解决组件间沟通的问题。


 

1.组件间的关系

1.1 父子组件

ReactJS中数据的流动是单向的,父组件的数据可以通过设置子组件的props传递数据给子组件。如果想让子组件改变父组件的数据,可以在父组件中传一个callback(回调函数)给子组件,子组件内调用这个callback即可改变父组件的数据。

var MyContainer = React.createClass({getInitialState: function(){return {list: ['item1', 'item2'],curItem: 'item1'}},// 改变curItem的回调函数changeItem: function(item){this.setState({curItem: item});},render: function(){return (<div>The curItem is: {this.state.curItem}<List list={this.state.list} changeItem={this.changeItem}/></div>)}
});var List = React.createClass({onClickItem: function(item){this.props.changeItem(item);},render: function(){return (<ul>{(function(){var self = this;return this.props.list.map(function(item){return (<li onClick={self.onClickItem.bind(self, item)}>I am {item}, click me!</li>		)});}.bind(this))()}</ul>)}
})ReactDOM.render(<MyContainer />,document.getElementById('example')
);

<MyContainer />是<List />的父组件,<MyContainer />通过props传递list数据给<List />组件,如果<MyContainer />中的list改变,<List />会重新渲染列表数据。而<List />可以通过<MyContainer />传来的changeItem函数,改变<MyContainer />的curItem数据。

 

1.2 兄弟组件

当两个组件不是父子关系,但有相同的父组件时,将这两个组件称为兄弟组件。兄弟组件不能直接相互传送数据,此时可以将数据挂载在父组件中,由两个组件共享:如果组件需要数据渲染,则由父组件通过props传递给该组件;如果组件需要改变数据,则父组件传递一个改变数据的回调函数给该组件,并在对应事件中调用。

var MyContainer = React.createClass({getInitialState: function(){return {list: ['item1', 'item2'],curItem: 'item1'}},// 改变curItem的回调函数changeItem: function(item){this.setState({curItem: item});},render: function(){return (<div>The curItem is: {this.state.curItem}<List list={this.state.list} curItem={this.state.curItem} /><SelectionButtons changeItem={this.changeItem}/></div>)}
});var List = React.createClass({render: function(){var selectedStyle = {color: 'white',background: 'red'};return (<ul>{(function(){var self = this;return this.props.list.map(function(item){var itemStyle = (item == self.props.curItem) ? selectedStyle : {};return (<li style={itemStyle}>I am {item}!</li>		)});}.bind(this))()}</ul>)}
});var SelectionButtons = React.createClass({onClickItem: function(item){this.props.changeItem(item);},render: function(){return (<div><button onClick={this.onClickItem.bind(this, 'item1')}>item1</button><button onClick={this.onClickItem.bind(this, 'item2')}>item2</button></div>)}
});ReactDOM.render(<MyContainer />,document.getElementById('example')
);
如上述代码所示,共享数据curItem作为state放在父组件<MyContainer />中,将回调函数changeItem传给<SelectionButtons />用于改变curItem,将curItem传给<List />用于高亮当前被选择的item。

 

2. 组件层次太深的噩梦

兄弟组件的沟通的解决方案就是找到两个组件共同的父组件,一层一层的调用上一层的回调,再一层一层地传递props。如果组件树嵌套太深,就会出现如下惨不忍睹的组件亲戚调用图。

share-parent-components

 

下面就来说说如何避免这个组件亲戚图的两个方法:全局事件和Context。

 

3. 全局事件

可以使用事件来实现组件间的沟通:改变数据的组件发起一个事件,使用数据的组件监听这个事件,在事件处理函数中触发setState来改变视图或者做其他的操作。使用事件实现组件间沟通脱离了单向数据流机制,不用将数据或者回调函数一层一层地传给子组件,可以避免出现上述的亲戚图。

事件模块可以使用如EventEmitter或PostalJS这些第三方库,也可以自己简单实现一个:


var EventEmitter = {_events: {},dispatch: function (event, data) {if (!this._events[event]) return; // no one is listening to this eventfor (var i = 0; i < this._events[event].length; i++)this._events[event][i](data);},subscribe: function (event, callback) {if (!this._events[event]) this._events[event] = []; // new eventthis._events[event].push(callback);},unSubscribe: function(event){if(this._events && this._events[event]) {delete this._events[event];}}
}


组件代码如下:


var MyContainer = React.createClass({render: function(){return (<div><CurItemPanel /><SelectionButtons/></div>)}
});var CurItemPanel = React.createClass({getInitialState: function(){return {curItem: 'item1'}},componentDidMount: function(){var self = this;EventEmitter.subscribe('changeItem', function(newItem){self.setState({curItem: newItem});})},componentWillUnmount: function(){EventEmitter.unSubscribe('changeItem');},render: function(){return (<p>The curItem is:  {this.state.curItem}</p>)}});var SelectionButtons = React.createClass({onClickItem: function(item){EventEmitter.dispatch('changeItem', item);},render: function(){return (<div><button onClick={this.onClickItem.bind(this, 'item1')}>item1</button><button onClick={this.onClickItem.bind(this, 'item2')}>item2</button></div>)}
});ReactDOM.render(<MyContainer />,document.getElementById('example')
);

 

事件绑定和解绑可以分别放在componentDidMount和componentWillUnMount中。由于事件是全局的,最好保证在componentWillUnMount中解绑事件,否则,下一次初始化组件时事件可能会绑定多次。 使用事件模型,组件之间无论是父子关系还是非父子关系都可以直接沟通,从而解决了组件间层层回调传递的问题,但是频繁地使用事件实现组件间沟通会使整个程序的数据流向越来越乱,因此,组件间的沟通还是要尽量遵循单向数据流机制。

 

4. context(上下文)

使用上下文可以让子组件直接访问祖先的数据或函数,无需从祖先组件一层层地传递数据到子组件中。

MyContainer组件:

var MyContainer = React.createClass({getInitialState: function(){return {curItem: 'item1'}},childContextTypes: {curItem: React.PropTypes.any,changeItem: React.PropTypes.any},getChildContext: function(){return {curItem: this.state.curItem,changeItem: this.changeItem}},changeItem: function(item){this.setState({curItem: item});},render: function(){return (<div><CurItemWrapper /><ListWrapper changeItem={this.changeItem}/></div>)}
});

childContextTypes用于验证上下文的数据类型,这个属性是必须要有的,否则会报错。getChildContext用于指定子组件可直接访问的上下文数据。

CurItemWrapper组件和CurItemPanel组件:

var CurItemWrapper = React.createClass({render: function(){return (<div><CurItemPanel /></div>)}
});var CurItemPanel = React.createClass({contextTypes: {curItem: React.PropTypes.any},render: function(){return (<p>The curItem is: {this.context.curItem}</p>)}});


在<CurItemPanel />通过this.context.curItem属性访问curItem,无需让<CurItemWrapper />将curItem传递过来。必须在contextTypes中设置curItem的验证类型,否则this.context是访问不了curItem的。

ListWrapper组件和List组件:

var ListWrapper = React.createClass({render: function(){return (<div><List /></div>)}
});var List = React.createClass({contextTypes: {changeItem: React.PropTypes.any},onClickItem: function(item){this.context.changeItem(item);},render: function(){return (<ul><li onClick={this.onClickItem.bind(this, 'item1')}>I am item1, click me!</li><li onClick={this.onClickItem.bind(this, 'item2')}>I am item2, click me!</li></ul>)}
});


同上,<List />可以通过this.context.changeItem获取<MyContainer />的改变curItem的changeItem函数。

 

5. Redux

为了在React中更加清晰地管理数据,Facebook提出了Flux架构,而redux则是Flux的一种优化实现。

关于redux,另外一个比我帅气的同事已经写了一篇详细的redux介绍博文,传送门在下面,有兴趣的可以去看看。

http://www.alloyteam.com/2015/09/react-redux/

 

当Redux与React搭配使用时,一般都是在最顶层组件中使用Redux。其余内部组件仅仅是展示性的,发起dispatch的函数和其他数据都通过props传入。然后,我们又会看到那熟悉的组件亲戚调用图:

share-parent-components

 

如果使用全局事件解决方案,那么redux中漂亮的,优雅的单向数据管理方式就会遭到破坏。于是,使用context就成了解决这种层层回调传递问题的首选方案,下面给出一个简单例子:

index.js:

import { createStore, applyMiddleware } from 'redux';
import reducers from "./reducers"
import { Provider } from 'react-redux'import React, {Component} from 'react';
import { render } from 'react-dom';
import App from './App';let store = createStore(reducers);render(<Provider store={store}><App /></Provider>,document.getElementById('root')
);


 

reducers.js:

export default function changeItem(state = {'curItem': 'item1'}, action){switch(action.type) {case 'CHANGE_ITEM':return Object.assign({}, {curItem: action.curItem});default:return state;}
}



actions.js:

export function changeItem(item) {return {type: 'CHANGE_ITEM',curItem: item}
}


App.js(组件代码):


import React, {Component} from 'react';
import { connect, Provider } from 'react-redux';
import { changeItem } from './actions';class App extends Component{constructor(props, context) {super(props, context);}getChildContext() {return {curItem: this.props.curItem,changeItem: this.props.changeItem}}render() {return (<div><CurItemPanel /><List /></div>)}
}App.childContextTypes = {curItem: React.PropTypes.any,changeItem: React.PropTypes.any
};class CurItemPanel extends Component {constructor(props, context) {super(props, context);}render() {return (<div>The curItem is: {this.context.curItem}</div>)}
}
CurItemPanel.contextTypes = {curItem: React.PropTypes.any
};class List extends Component {constructor(props, context) {super(props, context);}onClickItem (item){this.context.changeItem(item);}render() {return (<ul><li onClick={this.onClickItem.bind(this, 'item1')}>I am item1, click me!</li><li onClick={this.onClickItem.bind(this, 'item2')}>I am item2, click me!</li></ul>)}
}List.contextTypes = {changeItem: React.PropTypes.any
};let select = state => { return state};function mapDispatchToProps(dispatch) {return {changeItem: function(item) {dispatch(changeItem(item));}};
}export default(connect(select, mapDispatchToProps))(App);



上述代码中,Store是直接与智能组件<App />交互的,所以Store将state数据curItem和dispatch函数changeItem作为props传给了<App />。在<App />中将curItem数据和changeItem函数作为上下文,作为子组件的笨拙组件就可以之间通过上下文访问这些数据,无需通过props获取。

注:

1.redux的官方文档中是使用ES6语法的,所以这里的React代码也使用ES6做例子

2.运行上述代码需要构建代码,大家可以在redux的github中下载redux带构建代码的examples,然后将代码替换了再构建运行。

 

 

6. transdux

偶尔之间发现一个叫transdux的东西。这是一个类redux的数据沟通框架,作者的初衷是为了让用户写出比redux更简洁的代码,同时还能获得[fl|re]dux的好处。用户端使用该框架的话,可以解决下面一些redux中不好看的代码写法:

1)redux中需要创一个全局的store给Provider。Transdux中省略这个store。

2)redux与react搭配使用时,redux需要通过connect方法将数据和dispatch方法传给redux。Transdux没有connect。

3)redux需要把action当props传下去,跟传callback一样。Trandux不会出现这种传递。

 

使用transdux需要以下步骤

(1)安装trandux

npm install transdux –save

 

(2)把component包到Transdux里

import React, {Component} from 'react';
import Transdux from 'transdux';
import App from './TransduxApp.js';
import { render } from 'react-dom';render(<Transdux><App /></Transdux>,document.getElementById('root')
);

 

(3)定义component能干什么,component的状态如何改变


import React, {Component} from 'react';
import {mixin} from 'transdux'
import ChangeButton from './ChangeButton';// 定义action是怎么变的
let actions = {addHello(obj, state, props) {// 返回statereturn {msg: obj.msg}}
};class App extends Component{constructor(props){super(props);this.state = {msg: 'init'};}render() {// 应该传入调用了store.dispatch回调函数给笨拙组件return (<div>{this.state.msg}<ChangeButton /></div>)}
}export default mixin(App, actions);



(4)使用dispatch


import React, {Component} from 'react';
import {mixin} from 'transdux'
import minApp from './TransduxApp';
class ChangeButton extends Component{click() {this.dispatch(minApp, 'addHello', {'msg': 'hello world'});}render() {return (<div><button onClick={this.click.bind(this)}>change content</button></div>)}
}
export default mixin(ChangeButton, {});


mixin方法扩为<ChangeButton />扩展了一个dispatch方法。dispatch方法需要三个参数:接手消息的组件、改变组件的actions、传递的对象。<ChangeButton />的按钮事件处理函数调用了该dispatch后,会改变<App />中的状态。

 

使用了Clojure的Channel通信机制,实现了组件与组件之间的直接通信。这种通信的效果类似与events,每个组件可以维护着自己的state,然后用mixin包装自己传给其他组件改变状态。

 

Transdux的传送门在下面,有兴趣的同学可以去看看:

https://blog.oyanglul.us/javascript/react-transdux-the-clojure-approach-of-flux.html

 

小结

简单的的组件沟通可以用传props和callback的方法实现,然而,随着项目规模的扩大,组件就会嵌套得越来越深,这时候使用这个方法就有点不太适合。全局事件可以让组件直接沟通,但频繁使用事件会让数据流动变得很乱。如果兄弟组件共同的父组件嵌套得太深,在这个父组件设置context从而直接传递数据和callback到这两个兄弟组件中。使用redux可以让你整个项目的数据流向十分清晰,但是很容易会出现组件嵌套太深的情况,events和context都可以解决这个问题。Transdux是一个类redux框架,使用这个框架可以写出比redux简洁的代码,又可以得到redux的好处。

原博客地址:http://www.alloyteam.com/2016/01/some-methods-of-reactjs-communication-between-components/ 


转载于:https://www.cnblogs.com/songchunmin/p/7789670.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据可视化分析票房数据报告_票房收入分析和可视化

数据可视化分析票房数据报告Welcome back to my 100 Days of Data Science Challenge Journey. On day 4 and 5, I work on TMDB Box Office Prediction Dataset available on Kaggle.欢迎回到我的100天数据科学挑战之旅。 在第4天和第5天&#xff0c;我将研究Kaggle上提供的TM…

先知模型 facebook_Facebook先知

先知模型 facebook什么是先知&#xff1f; (What is Prophet?) “Prophet” is an open-sourced library available on R or Python which helps users analyze and forecast time-series values released in 2017. With developers’ great efforts to make the time-series …

搭建Maven私服那点事

摘要&#xff1a;本文主要介绍在CentOS7.1下使用nexus3.6.0搭建maven私服&#xff0c;以及maven私服的使用&#xff08;将自己的Maven项目指定到私服地址、将第三方项目jar上传到私服供其他项目组使用&#xff09; 一、简介 Maven是一个采用纯Java编写的开源项目管理工具, Mave…

gan训练失败_我尝试过(但失败了)使用GAN来创作艺术品,但这仍然值得。

gan训练失败This work borrows heavily from the Pytorch DCGAN Tutorial and the NVIDA paper on progressive GANs.这项工作大量借鉴了Pytorch DCGAN教程 和 有关渐进式GAN 的 NVIDA论文 。 One area of computer vision I’ve been wanting to explore are GANs. So when m…

19.7 主动模式和被动模式 19.8 添加监控主机 19.9 添加自定义模板 19.10 处理图形中的乱码 19.11 自动发现...

2019独角兽企业重金招聘Python工程师标准>>> 19.7 主动模式和被动模式 • 主动或者被动是相对客户端来讲的 • 被动模式&#xff0c;服务端会主动连接客户端获取监控项目数据&#xff0c;客户端被动地接受连接&#xff0c;并把监控信息传递给服务端 服务端请求以后&…

华盛顿特区与其他地区的差别_使用华盛顿特区地铁数据确定可获利的广告位置...

华盛顿特区与其他地区的差别深度分析 (In-Depth Analysis) Living in Washington DC for the past 1 year, I have come to realize how WMATA metro is the lifeline of this vibrant city. The metro network is enormous and well-connected throughout the DMV area. When …

Windows平台下kafka环境的搭建

近期在搞kafka&#xff0c;在Windows环境搭建的过程中遇到一些问题&#xff0c;把具体的流程几下来防止后面忘了。 准备工作&#xff1a; 1.安装jdk环境 http://www.oracle.com/technetwork/java/javase/downloads/index.html 2.下载kafka的程序安装包&#xff1a; http://kafk…

铺装s路画法_数据管道的铺装之路

铺装s路画法Data is a key bet for Intuit as we invest heavily in new customer experiences: a platform to connect experts anywhere in the world with customers and small business owners, a platform that connects to thousands of institutions and aggregates fin…

IBM推全球首个5纳米芯片:计划2020年量产

IBM日前宣布&#xff0c;该公司已取得技术突破&#xff0c;利用5纳米技术制造出密度更大的芯片。这种芯片可以将300亿个5纳米开关电路集成在指甲盖大小的芯片上。 IBM推全球首个5纳米芯片 IBM表示&#xff0c;此次使用了一种新型晶体管&#xff0c;即堆叠硅纳米板&#xff0c;将…

async 和 await的前世今生 (转载)

async 和 await 出现在C# 5.0之后&#xff0c;给并行编程带来了不少的方便&#xff0c;特别是当在MVC中的Action也变成async之后&#xff0c;有点开始什么都是async的味道了。但是这也给我们编程埋下了一些隐患&#xff0c;有时候可能会产生一些我们自己都不知道怎么产生的Bug&…

项目案例:qq数据库管理_2小时元项目:项目管理您的数据科学学习

项目案例:qq数据库管理Many of us are struggling to prioritize our learning as a working professional or aspiring data scientist. We’re told that we need to learn so many things that at times it can be overwhelming. Recently, I’ve felt like there could be …

react 示例_2020年的React Cheatsheet(+真实示例)

react 示例Ive put together for you an entire visual cheatsheet of all of the concepts and skills you need to master React in 2020.我为您汇总了2020年掌握React所需的所有概念和技能的完整视觉摘要。 But dont let the label cheatsheet fool you. This is more than…

查询数据库中有多少个数据表_您的数据中有多少汁?

查询数据库中有多少个数据表97%. That’s the percentage of data that sits unused by organizations according to Gartner, making up so-called “dark data”.97 &#xff05;。 根据Gartner的说法&#xff0c;这就是组织未使用的数据百分比&#xff0c;即所谓的“ 暗数据…

数据科学与大数据技术的案例_作为数据科学家解决问题的案例研究

数据科学与大数据技术的案例There are two myths about how data scientists solve problems: one is that the problem naturally exists, hence the challenge for a data scientist is to use an algorithm and put it into production. Another myth considers data scient…

Spring-Boot + AOP实现多数据源动态切换

2019独角兽企业重金招聘Python工程师标准>>> 最近在做保证金余额查询优化&#xff0c;在项目启动时候需要把余额全量加载到本地缓存&#xff0c;因为需要全量查询所有骑手的保证金余额&#xff0c;为了不影响主数据库的性能&#xff0c;考虑把这个查询走从库。所以涉…

leetcode 1738. 找出第 K 大的异或坐标值

本文正在参加「Java主题月 - Java 刷题打卡」&#xff0c;详情查看 活动链接 题目 给你一个二维矩阵 matrix 和一个整数 k &#xff0c;矩阵大小为 m x n 由非负整数组成。 矩阵中坐标 (a, b) 的 值 可由对所有满足 0 < i < a < m 且 0 < j < b < n 的元素…

商业数据科学

数据科学 &#xff0c; 意见 (Data Science, Opinion) “There is a saying, ‘A jack of all trades and a master of none.’ When it comes to being a data scientist you need to be a bit like this, but perhaps a better saying would be, ‘A jack of all trades and …

leetcode 692. 前K个高频单词

题目 给一非空的单词列表&#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c;按字母顺序排序。 示例 1&#xff1a; 输入: ["i", "love", "leetcode", "…

数据显示,中国近一半的独角兽企业由“BATJ”四巨头投资

中国的互联网行业越来越有被巨头垄断的趋势。百度、阿里巴巴、腾讯、京东&#xff0c;这四大巨头支撑起了中国近一半的独角兽企业。CB Insights日前发表了题为“Nearly Half Of China’s Unicorns Backed By Baidu, Alibaba, Tencent, Or JD.com”的数据分析文章&#xff0c;列…

Java的Servlet、Filter、Interceptor、Listener

写在前面&#xff1a; 使用Spring-Boot时&#xff0c;嵌入式Servlet容器可以通过扫描注解&#xff08;ServletComponentScan&#xff09;的方式注册Servlet、Filter和Servlet规范的所有监听器&#xff08;如HttpSessionListener监听器&#xff09;。 Spring boot 的主 Servlet…