Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS: Control a child state from the child and the parent

I have a rather simple problem and I'm not sure how to solve it with React's one way data flow.

Say you have a link in the parent that shows a modal

In the modal, you have an "X" that closes it.

I know I can change the state of the modal from the parent via props

// In the parent
<Modal display={this.state.showModal} />

// In the modal
<div className={this.props.display ? "show" : "hide"}>
  <a className="close">&times;</a>
  ...
</div>

And I know how to close the modal, but not both. Not sure how to keep a state that is shared and controllable by both the parent and the child modal.

UPDATE

In trying to keep this as modular as possible, I think the React way would be to store the open/close logic in the modal variable.

var ParentThing = React.createClass({
  ...
  render (
    <Modal /> // How can I call this.open in the modal from here?
  )
});

var Modal = React.createClass({
  setInitialState: function() {
    return {
      display: false
    }
  },
  close: function() {
    this.setState({ display: false });
  },
  open: function() {
    this.setState({ display: true });
  },
  render: function() {
    return (
      <div className={this.state.display ? "show" : "hide"}>
        <a className="close" onClick={this.close}>&times;</a>
      </div>
    )
  }
});

I saw this method, but it seems to be a little more than I need to do here. Reactjs: how to modify child state or props from parent?

like image 749
Adam Grant Avatar asked Apr 09 '15 01:04

Adam Grant


People also ask

How do you pass the state from parent to child in React?

To pass data from child to parent component in React:Pass a function as a prop to the Child component. Call the function in the Child component and pass the data as arguments. Access the data in the function in the Parent .

Can we pass data from child to parent in react JS?

While there is no direct way to pass data from the child to the parent component, there are workarounds. The most common one is to pass a handler function from the parent to the child component that accepts an argument which is the data from the child component. This can be better illustrated with an example.

Can you set state from a child component?

We can set Parent State from Children Component in ReactJs using the following approach. We will actually set the state of a parent in the parent component itself, but the children will be responsible for setting. We will create a function in parent to set the state with the given input.

Can we pass props from child to parent in React directly?

You can't pass props from child to parent in React, it's only one way (from parent to child). You should either: Put the state in the parent component and manipulate it from the child component by passing the setter function in the props.


2 Answers

You could pass a callback as a prop to the child component:

// In the parent
<Modal display={this.state.showModal} onClose={this.closeModal} />

// In the modal
<div className={this.props.display ? "show" : "hide"}>
  <a className="close" onClick={this.props.onClose}>&times;</a>
  ...
</div>

Then when you click the close button on the child, it will call the function of the parent

like image 116
Austin Greco Avatar answered Sep 28 '22 01:09

Austin Greco


There are two ways to handle this kind of thing in React:

  1. Make the child "controlled," just like a form input with a value and onChange property, where the owner of the input controls the input.
  2. Make the child "uncontrolled," just like a form input without a value.

The second choice seems faster up front, but just like managing a collection of form inputs in React, the advantage to using fully controlled components becomes apparent as complexity builds and the need to fully describe your UI at any point and time increases. (See this excellent answer from FakeRainBrigand if you're curious exactly why controlled components is better than uncontrolled in most cases.)

However, just like form inputs, there's no reason your component can't be either controlled or uncontrolled. If the user passes a display and onClose property, like Austin Greco's answer, you have a controlled modal, and the parent fully decides when to show or hide the modal.

If the user doesn't, you can skip using the properties and instead delegate to internal state managed by public methods on the modal component:

var ParentThing = React.createClass({
  ...
  render: function() {
    return <Modal ref="modal" />;
  },

  handleSomeClick: function() {
    this.refs.modal.open();
  }
});

var Modal = React.createClass({
  setInitialState: function() {
    return {
      display: false
    }
  },
  close: function() {
    this.setState({ display: false });
  },
  open: function() {
    this.setState({ display: true });
  },
  render: function() {
    return (
      <div className={this.state.display ? "show" : "hide"}>
        <a className="close" onClick={this.close}>&times;</a>
      </div>
    )
  }
});

If you like the idea of a controlled Modal component, but don't want to do all the boilerplate typing, you could even go so far as to implement something like the valueLink property for the Modal to simplify this pattern.

var ParentThing = React.createClass({
  ...
  mixins: [React.addons.LinkedStateMixin],

  getInitialState: function() {
    return { showModal: false };
  },

  render: function() {
    return <Modal displayLink={this.linkState("showModal")} />;
  },

  handleSomeClick: function() {
    this.setState({showModal: true});
  }
});

var Modal = React.createClass({
  close: function() {
    this.props.displayLink.requestChange(false);
  },

  render: function() {
    return (
      <div className={this.props.displayLink.value? "show" : "hide"}>
        <a className="close" onClick={this.close}>&times;</a>
      </div>
    )
  }
});

(See my blog post on creating custom components that work with linkState/valueLink for more info.)

So now you get the benefit of using a fully parent-controlled Modal, but you've removed a portion of the boilerplate around creating a function that sets the value to false and passing it to the modal.

like image 41
Michelle Tilley Avatar answered Sep 28 '22 00:09

Michelle Tilley