您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何解決React中組件通信問題,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
父子組件
父 → 子
parent組件傳給child組件,符合react的單向數據流理念,自上到下傳遞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,得到傳下來的值:{value} </div> ); } }
父組件做的就是定義好 state ,定義好事件函數,input onChange 的時候,去緩存 value 值,然后點擊 button 的時候,改變 state , 子組件只負責展示 value 。
子 → 父
child 組件通知 parent 組件, 主要是依靠 parent 傳下來的 callback 函數執行,改變 parent 組件的狀態,或者把 child 自己的 state 通知 parent 。分兩種情況:
state 定義在 parent 組件
// parent class 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 函數傳給 child ,child 組件自己處理內部的狀態(這里是表單的value值),當 child 組件分發消息的時候, 執行 parent 的 setValue 函數,從而改變了 parent 的 state,state發生變化, parent 組件執行 re-render 。
state 定義在 child 組件
// parent class Parent extends Component { onChange = value => { console.log(value, '來自 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> ); } }
有時候 state 是需要定義在 child 組件的,比如彈窗, CheckBox 這種開關性質的,邏輯是重復的,state 定義在組件內部更好維護, 復用性更好。但是 child 的 state 是需要告知我的 parent 組件的, 同樣還是執行 parent 傳下來的 change 函數。
兄弟組件
有時候可能出現頁面中的某兩部分通信,比如省市的級聯選擇,點擊 button 改變顏色等等,組件并不是父子級,沒有嵌套關系的時候。這種時候通常是依賴共有的頂級 Container 處理或者第三方的狀態管理器。其實原理都是相通的,兄弟 A 的 value 發生變化,分發的時候把 value 值告訴一個中間者 C ,C 會自動告知 B,實現 B 的自動render 。
利用共有的Container
// container class 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> ); } }
// 兄弟A class 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> ) } }
// 兄弟B const B = props => ( <div className="card"> 我是Brother B, value是: {props.value} </div> ); export default B;
組件 A 中的表單 value 值,告知了父級 Container 組件(通過 setValue 函數改變 state),組件 B 依賴于 Container 傳下來的 state,會做出同步更新。這里的中間者是 Container。
利用Context
上面的方式,如果嵌套少還可以,如果嵌套特別多,比如一級導航欄下的二級導航欄下的某個按鈕,要改變頁面中 content 區域的 table 里的某個列的值...他們同屬于一個 page 。這樣傳遞 props 就會很痛苦,每一層組件都要傳遞一次。
// 頂級公共組件
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 的 parent class AParent extends Component { render() { return ( <div className="card"> <A /> </div> ); } } // A class 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 的 parent class BParent extends Component { render() { return ( <div className="card"> <B /> </div> ); } } // B class B extends Component { render() { return ( <div> 我是parentB 下的 B, value是: {this.context.value} </div> ); } } B.contextTypes = { value: PropTypes.string, };
組件 A 仍是 消息的發送者,組件 B 是接收者, 中間者是 Context 公有 Container 組件。context是官方文檔的一個 API ,通過 getChildContext 函數定義 context 中的值,并且還要求 childContextTypes 是必需的。這樣屬于這個 Container 組件的子組件,通過 this.context 就可以取到定義的值,并且起到跟 state 同樣的效果。中間者其實還是 Container,只不過利用了上下文這樣的 API ,省去了 props 的傳遞。另外:這個功能是實驗性的,未來可能會有所改動。
發布訂閱
這種一個地方發送消息,另一個地方接收做出變化的需求,很容易想到的就是觀察者模式了。具體的實現會有很多種,這里我們自己寫了一個 EventEmitter 的類(其實就是仿照 node 中的 EventEmitter 類),如果不了解觀察者,可以看我的另一篇文章 觀察者模式 。
// 發布訂閱類 class EventEmitter { _event = {} // on 函數用于綁定 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 用于分發消息 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 };
// Container import A from './a'; import B from './b'; const Listener = () => { return ( <div> <A /> <B /> </div> ); }; export default Listener;
// 兄弟組件 A import { 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> ) } }
// 兄弟組件 B import { 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 用于分發消息,組件 B 去接收消息。這里的中間者其實就是 event 對象。需要接收消息的 B 去訂閱 dispatch 事件,并把回調函數 valueChange 傳入,另外 B 定義了自己的 state,方便得到 value 值的時候自動渲染。組件 A 其實就是把內部的表單 value 在點擊的時候分發,發布事件,從而 B 中的 valueChange 執行,改變 state。這種方式比較方便,也更直觀,不需要借助 Container 組件去實現,省去了很多邏輯。
Redux || Mobx
Redux 或者 Mobx 是第三方的狀態管理器,是這里我們通信的中間者。大型項目最直接的就是上庫... 更方便,更不容易出錯。 但其實小項目就沒什么必要了。東西比較多,這里不再闡述它們的實現和做了什么。
關于“如何解決React中組件通信問題”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。