简体   繁体   中英

The proper way of exchanging objects between sibling components in React

I am trying to build a simple React app and stuck into trouble. I have the following structure:

App.js

class App extends React.Component {
  render() {
    return (
      <div className="App">
        <PlanBuilder />
        <PlanMenu />
      </div>
    );
  }
}

PlanMenu.js

class PlanMenu extends React.Component {
  render() {
    return (
      <div className="PlanMenu">
        <button type="button"
          onClick={addObject(
            new CWall({
                x: 100,
                y: 100,
                length: 200
            }))}>Wall
        </button>
      </div>
    );
  }
}

PlanBuilder.js

class PlanBuilder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      objects: []
    };
  }

  addObject(object) {
    this.setState({
        objects: [...this.state.objects, object]
    });
  }

  render() {
    return (
      <Stage>
        <Layer>
          {
          this.state.objects.map(function(object) {
              return object.render();
            })
          }
        </Layer>
      </Stage>
    );
}

So the main idea is that I have two sibling components: the drawing area and the menu. When the button on the menu is pressed, I want to create a new object and send it to the drawing area element. So, the question is how to pass PlanBuilder.addObject method to the PlanMenu class. I came from the C world, and what I think about is to pass kinda function pointer to PlanMenu . However, I am not sure this is an appropriate solution. Would you please recommend me the proper way of doing this in React? Thank you in advance.

In this case you have two ways.

The simpler one is to move the logic you have on PlanBuilder to App, and pass the necessary props to PlanBuilder and PlanMenu, like:

class PlanMenu extends React.Component {
  render() {
    const { addObject } = this.props

    return (
      <div className="PlanMenu">
        <button type="button"
          onClick={addObject(
            new CWall({
                x: 100,
                y: 100,
                length: 200
            }))}>Wall
        </button>
      </div>
    );
  }
}

class PlanBuilder extends React.Component {
  render() {
    const { objects } = this.props

    return (
      <Stage>
        <Layer>
          {objects.map(function(object) {
            return object.render();
          })}
        </Layer>
      </Stage>
    )
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      objects: []
    };

    this.addObject = this.addObject.bind(this)
  }

  addObject(object) {
    this.setState({
        objects: [...this.state.objects, object]
    });
  }

  render() {
    const { objects } = this.state

    return (
      <div className="App">
        <PlanBuilder objects={objects} />
        <PlanMenu addObject={this.addObject} />
      </div>
    );
  }
}

The other alternative is to create a "Container" to hold the logic instead adding it to App, like:

class PlanMenu extends React.Component {
  render() {
    const { addObject } = this.props

    return (
      <div className="PlanMenu">
        <button type="button"
          onClick={addObject(
            new CWall({
                x: 100,
                y: 100,
                length: 200
            }))}>Wall
        </button>
      </div>
    );
  }
}

class PlanBuilder extends React.Component {
  render() {
    const { objects } = this.props

    return (
      <Stage>
        <Layer>
          {objects.map(function(object) {
            return object.render();
          })}
        </Layer>
      </Stage>
    )
  }
}

class PlanContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      objects: []
    };

    this.addObject = this.addObject.bind(this)
  }

  addObject(object) {
    this.setState({
        objects: [...this.state.objects, object]
    });
  }

  render() {
    const { objects } = this.state

    return (
      <div>
        <PlanBuilder objects={objects} />
        <PlanMenu addObject={this.addObject} />
      </div>
    )
  }
}

class App extends React.Component {

  render() {
    return (
      <div className="App">
        <PlanContainer />
      </div>
    );
  }
}

In my opinion, creating a Container makes your code more readable, reusable and cleaner :)

Hope it help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM