React中无需中间组件与Redux,实现孙组件向祖父组件传值
当然可行!两种不依赖中间组件/Redux的实现方案
完全可以实现GrandChild直接向AboutUs传递状态,不需要借助AboutUsChild当“传声筒”,也不用Redux这类状态管理库。下面给你两种实用的方案,都是基于你提供的代码修改的:
方案一:React Context API(官方推荐)
Context API就是React官方为跨层级传递数据/方法设计的,完美匹配你的场景:
1. 创建Context
首先创建一个Context实例,用来承载祖父组件的状态更新方法:
import React, { Component, createContext } from 'react'; // 创建Context const ChildToParentContext = createContext();
2. 在AboutUs中提供Context
修改AboutUs组件,用ChildToParentContext.Provider包裹子组件,把更新状态的方法传递下去:
class AboutUs extends Component { constructor(props) { super(props); this.state = { childState: false }; } // 改用箭头函数,避免手动绑定this myMethod = (value) => { this.setState({ childState: value }); }; render() { console.log('AboutUs', this.state.childState); return ( {/* 用Provider包裹所有子组件,传递更新方法 */} <ChildToParentContext.Provider value={this.myMethod}> <div> <h1 className={`title ${this.state.childState ? 'on' : ''}`}> About us {this.props.aboutusProp} </h1> <AboutUsChild /> </div> </ChildToParentContext.Provider> ); } }
3. 在GrandChild中消费Context
修改GrandChild组件,直接从Context中获取祖父的方法,完全不需要中间组件传递props:
class GrandChild extends Component { constructor() { super(); this.state = { TitleClick: false }; } TitleClick = () => { const newState = !this.state.TitleClick; this.setState({ TitleClick: newState }); // 直接调用Context里的方法更新祖父组件状态 this.context(newState); }; // 声明该组件要使用的Context static contextType = ChildToParentContext; render() { console.log('GrandChild', this.state.TitleClick); return ( <div> {this.props.Method ? ( <h1 onClick={this.TitleClick} className={`title ${this.state.TitleClick ? 'on' : ''}`} > Grand Child </h1> ) : null} <h1>{this.props.children}</h1> </div> ); } }
修改后,AboutUsChild完全不用处理任何状态或方法的传递,GrandChild直接和AboutUs建立联系完成状态更新。
方案二:自定义事件总线(轻量快捷)
如果觉得Context有点繁琐,也可以用自定义事件的方式,本质是利用浏览器的事件监听机制:
1. 实现事件总线
可以单独写个工具对象,或者直接在组件里定义:
// 简单的事件总线实现 const eventBus = { // 监听事件 on(event, callback) { document.addEventListener(event, callback); }, // 触发事件 emit(event, data) { document.dispatchEvent(new CustomEvent(event, { detail: data })); }, // 移除监听,防止内存泄漏 off(event, callback) { document.removeEventListener(event, callback); } };
2. 在AboutUs中监听事件
修改AboutUs组件,在挂载时监听孙组件的更新事件,卸载时移除监听:
class AboutUs extends Component { constructor(props) { super(props); this.state = { childState: false }; this.handleGrandChildUpdate = this.handleGrandChildUpdate.bind(this); } handleGrandChildUpdate(e) { // 从事件对象中获取孙组件传递的状态 this.setState({ childState: e.detail }); } componentDidMount() { // 监听孙组件的更新事件 eventBus.on('grandchild:state-update', this.handleGrandChildUpdate); } componentWillUnmount() { // 组件卸载时一定要移除监听,避免内存泄漏 eventBus.off('grandchild:state-update', this.handleGrandChildUpdate); } render() { console.log('AboutUs', this.state.childState); return ( <div> <h1 className={`title ${this.state.childState ? 'on' : ''}`}> About us {this.props.aboutusProp} </h1> <AboutUsChild /> </div> ); } }
3. 在GrandChild中触发事件
修改GrandChild组件,点击标题时触发事件传递状态:
class GrandChild extends Component { constructor() { super(); this.state = { TitleClick: false }; } TitleClick = () => { const newState = !this.state.TitleClick; this.setState({ TitleClick: newState }); // 触发事件,把新状态传出去 eventBus.emit('grandchild:state-update', newState); }; render() { console.log('GrandChild', this.state.TitleClick); return ( <div> {this.props.Method ? ( <h1 onClick={this.TitleClick} className={`title ${this.state.TitleClick ? 'on' : ''}`} > Grand Child </h1> ) : null} <h1>{this.props.children}</h1> </div> ); } }
这种方式完全绕过中间组件,但一定要注意在组件卸载时移除事件监听,避免内存泄漏。
总结
- 优先推荐Context API:它是React官方方案,更符合React的设计模式,类型安全,维护起来也更省心。
- 事件总线适合简单场景:轻量快捷,但大型项目里可能会导致事件逻辑混乱,需要谨慎使用。
内容的提问来源于stack exchange,提问作者Roy
相关产品推荐
相关产品推荐

