国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

詳解React中的組件通信問(wèn)題

2019-11-19 15:56:28
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

引入

本來(lái)我是沒(méi)想過(guò)總結(jié)這些東西的,會(huì)感覺(jué)比較入門(mén)。但是之前同學(xué)去騰訊面試問(wèn)到了這個(gè)問(wèn)題(react或vue的組件通信),我?guī)退恚槺銓?xiě)demo的過(guò)程中,會(huì)有一些新的體會(huì),多總結(jié)還是有利于進(jìn)步的呀。

父子組件

父 → 子

parent組件傳給child組件,符合react的單向數(shù)據(jù)流理念,自上到下傳遞props。

// 父組件class Parent extends Component { constructor() {  super();  this.state = {   value: '',  } } handleChange = e => {  this.value = e.target.value; } handleClick = () => {  this.setState({   value: this.value,  }) } render() {  return (   <div>    我是parent    <input onChange={this.handleChange} />    <div className="button" onClick={this.handleClick}>通知</div>    <div>     <Child value={this.state.value} />    </div>    </div>  ); }}
// 子組件class Child extends Component { render() {  const { value } = this.props;  return (   <div>    我是Child,得到傳下來(lái)的值:{value}   </div>  ); }}

父組件做的就是定義好 state ,定義好事件函數(shù),input onChange 的時(shí)候,去緩存 value 值,然后點(diǎn)擊 button 的時(shí)候,改變 state , 子組件只負(fù)責(zé)展示 value 。

子 → 父

child 組件通知 parent 組件, 主要是依靠 parent 傳下來(lái)的 callback 函數(shù)執(zhí)行,改變 parent 組件的狀態(tài),或者把 child 自己的 state 通知 parent 。分兩種情況:

state 定義在 parent 組件

// parentclass Parent extends Component { constructor() {  super();  this.state = {   value: '',  } } setValue = value => {  this.setState({   value,  }) } render() {  return (   <div>    <div>我是parent, Value是:{this.state.value}</div>     <Child setValue={this.setValue} />   </div>  ); }}
class Child extends Component { handleChange = e => {  this.value = e.target.value; } handleClick = () => {  const { setValue } = this.props;  setValue(this.value); } render() {  return (   <div>    我是Child    <div className="card">     state 定義在 parent     <input onChange={this.handleChange} />     <div className="button" onClick={this.handleClick}>通知</div>    </div>   </div>  ); }}

parent 組件把改變 state 的 setValue 函數(shù)傳給 child ,child 組件自己處理內(nèi)部的狀態(tài)(這里是表單的value值),當(dāng) child 組件分發(fā)消息的時(shí)候, 執(zhí)行 parent 的 setValue 函數(shù),從而改變了 parent 的 state,state發(fā)生變化, parent 組件執(zhí)行 re-render 。

state 定義在 child 組件

// parentclass Parent extends Component { onChange = value => {  console.log(value, '來(lái)自 child 的 value 變化'); } render() {  return (   <div>    <div>我是parent    <Child onChange={this.onChange} />   </div>  ); }}
class Child extends Component { constructor() {  super();  this.state = {   childValue: ''  } } childValChange = e => {  this.childVal = e.target.value; } childValDispatch = () => {  const { onChange } = this.props;  this.setState({   childValue: this.childVal,  }, () => { onChange(this.state.childValue) }) } render() {  return (   <div>    我是Child    <div className="card">     state 定義在 child     <input onChange={this.childValChange} />     <div className="button" onClick={this.childValDispatch}>通知</div>    </div>   </div>  ); }}

有時(shí)候 state 是需要定義在 child 組件的,比如彈窗, CheckBox 這種開(kāi)關(guān)性質(zhì)的,邏輯是重復(fù)的,state 定義在組件內(nèi)部更好維護(hù), 復(fù)用性更好。但是 child 的 state 是需要告知我的 parent 組件的, 同樣還是執(zhí)行 parent 傳下來(lái)的 change 函數(shù)。

兄弟組件

有時(shí)候可能出現(xiàn)頁(yè)面中的某兩部分通信,比如省市的級(jí)聯(lián)選擇,點(diǎn)擊 button 改變顏色等等,組件并不是父子級(jí),沒(méi)有嵌套關(guān)系的時(shí)候。這種時(shí)候通常是依賴(lài)共有的頂級(jí) Container 處理或者第三方的狀態(tài)管理器。其實(shí)原理都是相通的,兄弟 A 的 value 發(fā)生變化,分發(fā)的時(shí)候把 value 值告訴一個(gè)中間者 C ,C 會(huì)自動(dòng)告知 B,實(shí)現(xiàn) B 的自動(dòng)render 。

利用共有的Container

// containerclass Container extends Component { constructor() {  super();  this.state = {   value: '',  } } setValue = value => {  this.setState({   value,  }) } render() {  return (   <div>    <A setValue={this.setValue}/>    <B value={this.state.value} />   </div>  ); }}
// 兄弟Aclass A extends Component { handleChange = (e) => {  this.value = e.target.value; } handleClick = () => {  const { setValue } = this.props;  setValue(this.value); } render() {  return (   <div className="card">    我是Brother A, <input onChange={this.handleChange} />    <div className="button" onClick={this.handleClick}>通知</div>   </div>  ) }}
// 兄弟Bconst B = props => ( <div className="card">  我是Brother B, value是:  {props.value} </div>);export default B;

組件 A 中的表單 value 值,告知了父級(jí) Container 組件(通過(guò) setValue 函數(shù)改變 state),組件 B 依賴(lài)于 Container 傳下來(lái)的 state,會(huì)做出同步更新。這里的中間者是 Container。

利用Context

上面的方式,如果嵌套少還可以,如果嵌套特別多,比如一級(jí)導(dǎo)航欄下的二級(jí)導(dǎo)航欄下的某個(gè)按鈕,要改變頁(yè)面中 content 區(qū)域的 table 里的某個(gè)列的值...他們同屬于一個(gè) page 。這樣傳遞 props 就會(huì)很痛苦,每一層組件都要傳遞一次。

// 頂級(jí)公共組件
class Context extends Component {

 

 constructor() {  super();  this.state = {   value: '',  }; } setValue = value => {  this.setState({   value,  }) } getChildContext() { // 必需  return {    value: this.state.value,   setValue: this.setValue,  }; } render() {  return (   <div>    <AParent />    <BParent />   </div>  ); }}// 必需Context.childContextTypes = { value: PropTypes.string, setValue: PropTypes.func,};
// A 的 parentclass AParent extends Component { render() {  return (   <div className="card">    <A />   </div>  ); }}// Aclass A extends Component { handleChange = (e) => {  this.value = e.target.value; } handleClick = () => {  const { setValue } = this.context;  setValue(this.value); } render() {  return (   <div>    我是parentA 下的 A, <input onChange={this.handleChange} />    <div className="button" onClick={this.handleClick}>通知</div>   </div>  ); }}// 必需A.contextTypes = { setValue: PropTypes.func,};
// B 的 parentclass BParent extends Component { render() {  return (   <div className="card">    <B />   </div>  ); }}// Bclass B extends Component { render() {  return (   <div>    我是parentB 下的 B, value是:    {this.context.value}   </div>  ); }}B.contextTypes = { value: PropTypes.string,};

組件 A 仍是 消息的發(fā)送者,組件 B 是接收者, 中間者是 Context 公有 Container 組件。context是官方文檔的一個(gè) API ,通過(guò) getChildContext 函數(shù)定義 context 中的值,并且還要求 childContextTypes 是必需的。這樣屬于這個(gè) Container 組件的子組件,通過(guò) this.context 就可以取到定義的值,并且起到跟 state 同樣的效果。中間者其實(shí)還是 Container,只不過(guò)利用了上下文這樣的 API ,省去了 props 的傳遞。另外:這個(gè)功能是實(shí)驗(yàn)性的,未來(lái)可能會(huì)有所改動(dòng)。

發(fā)布訂閱

這種一個(gè)地方發(fā)送消息,另一個(gè)地方接收做出變化的需求,很容易想到的就是觀(guān)察者模式了。具體的實(shí)現(xiàn)會(huì)有很多種,這里我們自己寫(xiě)了一個(gè) EventEmitter 的類(lèi)(其實(shí)就是仿照 node 中的 EventEmitter 類(lèi)),如果不了解觀(guān)察者,可以看我的另一篇文章 觀(guān)察者模式 。

// 發(fā)布訂閱類(lèi)class EventEmitter { _event = {} // on 函數(shù)用于綁定 on(eventName, handle) {  let listeners = this._event[eventName];  if(!listeners || !listeners.length) {   this._event[eventName] = [handle];   return;  }  listeners.push(handle); } // off 用于移除 off(eventName, handle) {  let listeners = this._event[eventName];  this._event[eventName] = listeners.filter(l => l !== handle); } // emit 用于分發(fā)消息 emit(eventName, ...args) {  const listeners = this._event[eventName];  if(listeners && listeners.length) {   for(const l of listeners) {    l(...args);   }  } }}const event = new EventEmitter;export { event };
// Containerimport A from './a';import B from './b';const Listener = () => { return (  <div>   <A />   <B />  </div> );};export default Listener;
// 兄弟組件 Aimport { event } from './eventEmitter';class A extends Component { handleChange = e => {  this.value = e.target.value; } handleClick = () => {  event.emit('dispatch', this.value); } render() {  return (   <div className="card">    我是Brother A, <input onChange={this.handleChange} />    <div className="button" onClick={this.handleClick}>通知</div>   </div>  ) }}
// 兄弟組件 Bimport { event } from './eventEmitter';class B extends Component { state = {  value: '' } componentDidMount() {  event.on('dispatch', this.valueChange); } componentWillUnmount() {  event.off('dispatch', this.valueChange); } valueChange = value => {  this.setState({   value,  }) } render() {  return (   <div className="card">    我是Brother B, value是:    {this.state.value}   </div>  ); }}

仍然是組件 A 用于分發(fā)消息,組件 B 去接收消息。這里的中間者其實(shí)就是 event 對(duì)象。需要接收消息的 B 去訂閱 dispatch 事件,并把回調(diào)函數(shù) valueChange 傳入,另外 B 定義了自己的 state,方便得到 value 值的時(shí)候自動(dòng)渲染。組件 A 其實(shí)就是把內(nèi)部的表單 value 在點(diǎn)擊的時(shí)候分發(fā),發(fā)布事件,從而 B 中的 valueChange 執(zhí)行,改變 state。這種方式比較方便,也更直觀(guān),不需要借助 Container 組件去實(shí)現(xiàn),省去了很多邏輯。

Redux || Mobx

Redux 或者 Mobx 是第三方的狀態(tài)管理器,是這里我們通信的中間者。大型項(xiàng)目最直接的就是上庫(kù)... 更方便,更不容易出錯(cuò)。 但其實(shí)小項(xiàng)目就沒(méi)什么必要了。東西比較多,這里不再闡述它們的實(shí)現(xiàn)和做了什么。

總結(jié)

react 特殊的自上而下的單向數(shù)據(jù)流,和 state 的特性,造就以這樣的思想實(shí)現(xiàn)組件通信。除去發(fā)布訂閱和 Redux 等,其他的都是 props 自上而下傳遞的理念,子組件需要的總是通過(guò)父組件傳遞下來(lái)的,關(guān)于 state 的定義,還是看具體的應(yīng)用場(chǎng)景了。

另外本次的代碼都放在https://github.com/sunyongjian/rc-communication-demo, 可以 done 下來(lái)加深理解。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 德令哈市| 长岭县| 天津市| 安多县| 芦山县| 鹤岗市| 县级市| 门源| 虎林市| 新泰市| 古浪县| 洪洞县| 靖江市| 博爱县| 九江市| 周宁县| 抚顺县| 化隆| 吉安市| 房山区| 余庆县| 临沭县| 克拉玛依市| 碌曲县| 凤台县| 清原| 山东省| 宣威市| 时尚| 秦皇岛市| 八宿县| 会理县| 娄底市| 大渡口区| 三穗县| 固镇县| 澄江县| 崇明县| 佛学| 丹棱县| 阜新|