React中兄弟组件间传递对象的正确方式:组件方法传递方案咨询
Hey there! Coming from a C background, it makes total sense to think in terms of function pointers, but React has its own idiomatic way to handle this kind of component communication. Since PlanBuilder and PlanMenu are sibling components (both children of App), they can't directly call each other's methods—you need to use their parent component as a middleman. Here are two React-compliant solutions to get this working:
Solution 1: State Lifting (Recommended for Unidirectional Data Flow)
This is the React-preferred approach: move the shared state (objects) up to the parent App component, then pass both the state and the method to modify it down to the child components. This keeps state management centralized and follows React's unidirectional data flow principle.
Updated App.js
class App extends React.Component { constructor(props) { super(props); this.state = { objects: [] }; // Bind `this` to ensure the method works correctly in child components this.addObject = this.addObject.bind(this); } addObject(object) { this.setState({ objects: [...this.state.objects, object] }); } render() { return ( <div className="App"> {/* Pass the objects state to PlanBuilder for rendering */} <PlanBuilder objects={this.state.objects} /> {/* Pass the addObject method to PlanMenu for triggering additions */} <PlanMenu addObject={this.addObject} /> </div> ); } }
Updated PlanBuilder.js
class PlanBuilder extends React.Component { render() { return ( <Stage> <Layer> {this.props.objects.map((object) => object.render())} </Layer> </Stage> ); } }
Updated PlanMenu.js
class PlanMenu extends React.Component { render() { return ( <div className="PlanMenu"> {/* Wrap the call in an arrow function to avoid immediate execution on render */} <button type="button" onClick={() => this.props.addObject(new CWall({ x: 100, y: 100, length: 200 }))} > Wall </button> </div> ); } }
Solution 2: Using Refs to Access Child Component Instance (For Simple Scenarios)
If you prefer not to lift state, you can use React refs to get a reference to the PlanBuilder instance, then pass its addObject method to PlanMenu. Note that this approach couples your components more tightly and breaks unidirectional flow, so use it sparingly.
Updated App.js
class App extends React.Component { constructor(props) { super(props); // Create a ref to hold the PlanBuilder instance this.planBuilderRef = React.createRef(); } render() { return ( <div className="App"> <PlanBuilder ref={this.planBuilderRef} /> {/* Pass a wrapper function that calls PlanBuilder's addObject */} <PlanMenu addObject={(obj) => this.planBuilderRef.current.addObject(obj)} /> </div> ); } }
PlanMenu.js remains the same as in Solution 1
class PlanMenu extends React.Component { render() { return ( <div className="PlanMenu"> <button type="button" onClick={() => this.props.addObject(new CWall({ x: 100, y: 100, length: 200 }))} > Wall </button> </div> ); } }
Critical Note
Your original PlanMenu code had a bug: onClick={addObject(new CWall(...))} calls the function immediately when the component renders, not when the button is clicked. Wrapping the call in an arrow function (as shown above) fixes this by ensuring the function only runs on click.
内容的提问来源于stack exchange,提问作者kasom

