grabury
grabury

Reputation: 5559

How to make a React component ‘communicate’ with another React component?

I have a parent jsx component that has 2 different jsx components within it. The one component is a button and the other is a div that opens and closes itself when you click on it (it has a click handler and a state of open or closed). I now want to add the ability for the button to open and close the div as well. Is the only way to accomplish this is to pass a handler function down to the button from the parent, moving the div’s open and closed state to the parent component, and pass the state down to the div as props? The reason I ask is that this particular div component is used in a number of different components and removing the open and closed state would affect a lot of different parent components.

Upvotes: 0

Views: 327

Answers (2)

Kunukn
Kunukn

Reputation: 2236

Here's a code example of allowing external state manipulation where you can mix the usage of the button or the div to toggle the state. You extend your Collapsible component to use passed props to update the state.

class Collapsible extends React.Component {
  constructor(props){
    super(props);
    this.state = { isOpen: this.props.isOpen !== false };
    this.toggleOpen = this.toggleOpen.bind(this);
  }
  componentWillReceiveProps({ isOpen }) {
    this.setState({ isOpen });
  }
  toggleOpen(){
   this.setState((prevState) => ({ isOpen: !prevState.isOpen }))
  }
  render() {
    let display = this.state.isOpen ? null : "none";
    return (
      <div
        className="collapsible"
        onClick={this.toggleOpen}
      >
        <header> header </header>
        <div style={{ display }}>{this.props.children}</div>
      </div>
    );
  }
}

class Parent extends React.Component {
  constructor(props){
    super(props);
    this.state = { isOpen: true };
    this.toggleOpen = this.toggleOpen.bind(this);
  }

  toggleOpen(){
   this.setState((prevState) => ({ isOpen: !prevState.isOpen }))
  }

  render() {
    return (
      <div className="parent">
        <Collapsible isOpen={this.state.isOpen}>content</Collapsible>
        <button onClick={this.toggleOpen}>
          toggle
        </button>
      </div>
    );
  }
}

Upvotes: 1

Mr. Baudin
Mr. Baudin

Reputation: 2254

Here is another code example, hope it helps:

Edit 3xrw9vny8m

I use the local state of the Container and pass this down to the child components. In a bigger app I'd advice you to use something like Redux to manage your state.

The central idea is that the parent component passes a function which can "change it's state" to the button child. It also passed the current isOpen state to the panel. Clicking the button will change the state of the parent, thus triggering a reflow, thus updating the collapsable.

For future reference:

import React from "react";
import { render } from "react-dom";

const Collapsable = ({ isOpen }) =>
  isOpen ? (
    <div
      style={{
        border: "1px solid orange",
        padding: "1rem",
        background: "maroon",
        color: "white"
      }}
    >
      {" "}
      Hey, I'm open{" "}
    </div>
  ) : (
    <div>Oh no...closed :(</div>
  );

const Button = ({ openPanel }) => (
  <button onClick={() => openPanel()}>Open Panel</button>
);

class Container extends React.PureComponent {
  state = { open: false };

  openPanel = () => {
    this.setState({
      ...this.state,
      open: this.state.open ? false : true
    });
  };

  render() {
    return (
      <div>
        <Button openPanel={this.openPanel} />
        <Collapsable isOpen={this.state.open} />
      </div>
    );
  }
}

const App = () => (
  <div>
    <Container />
  </div>
);

render(<App />, document.getElementById("root"));

Upvotes: 0

Related Questions