2

My structure is as such:

       <ParentComponent />
 <Child 1 />     <Child 2 />

I have a function in <Child 1 />. Since <Child 1 /> controls the grid-layout, and <Child 2 /> is a Navbar with buttons, I want to have a button in <Child 2 /> which will reset some parameters in <Child 1 />.

How do I achieve this? As far as I can see, refs will only be able to go one "step" up the tree, not down.

There isn't any obvious way this function can be invoked from the parent component. Is there any way here? All the solutions I can think of aren't really following the React-mindset, namely the unidirectional data-flow.

cbll
  • 6,499
  • 26
  • 74
  • 117

3 Answers3

5

You can make the parent component as a container for both components. So all the states and functions are handled in the parent components and pass them to the other components as props.

e.g.

    class Parent extends React.Component {

         constructor() {
             super();
             this.state = {
                 controls: controls
             }
         }

         onClick = (dataFromChild2) => {
              //Resetting
              this.setState({controls: dataFromChild2})
         }

         render() {
             return (
                 <div>
                      <Child1 gridControl={this.state.controls}/>
                      <Child2 onClick={this.onClick}/>
                 </div>
             )
         }
    }

You can access the gridControl and onClick from this.props in the children components

UPDATE

Think of it this way, you have the Parent component with the states and function needed to handle the data. The children components take those data and update their states accordingly.

Let's Say the Parent Component is something like this:

class Parent extends React.Component {

  constructor() {
    super();
    this.state = {
      gridControl: {}
    }
  }

  onChild2ButtonClick = (dataFromChild2) => {
    this.setState({
      gridControl: dataFromChild2
    });
  };

  render() {
    return (
      <div>
        <Child1 controls={this.state.gridControl}/>
        <Child2 onClick={this.onChild2ButtonClick}/>
      </div>
    );
  }
}

Child2 Component is something like this:

 class Child2 extends React.Component {
  constructor() {
    super();
  }

  onClick = () => {
    var data = {};
    this.props.onClick(data);
  };

  render() {
    return (
      <div>
        <button onClick={this.onClick}/>
      </div>
    );
  }

If you're using states for Child1, and don't want to change them to props with function in the Parent component to handle them, then you update the state in the componentWillReceivePropsmethod with the new props received from the parent component, so that the props sent will match the states used in Child1 component.

Hope this will clear things up.

  • The state of the grid layout is fully controlled inside Child 1. I don't see how I can pass it as a prop from the parent, it it's manipulated in a lot of functions in there. I'm also not quite sure I get the data flow from your example. Child 2 would push the state back up? Are you supposed to do that? – cbll Apr 27 '17 at 12:39
  • And how do you pass "datafromchild2" to a function in a parent component like that? – cbll Apr 27 '17 at 12:53
  • in the Child2 component, when calling the onClick, you can send the data with it. so it would be something like this `this.props.onClick(data)`. Since you're not using store system. I think this would do the trick. you can update the the states in the Child1 component using `componentWillReceiveProps` lifecycle method. – Mamdouh Alsarayreh Apr 27 '17 at 12:54
  • I'm not sure I understand. The "controls"(from your example) is a saved state inside Child 1. I don't think it works if it's passed as props, since to draw the grid-layout, it's manipulating state in several functions, amongst other things saving it to localstorage(html5). Also the onClick function is inside a button rendered in a div in Child 2... Can you really pass it that far up? – cbll Apr 27 '17 at 12:57
  • You don't need to change the states in Child1 component. All you need to do is set the prop `gridControl` to the state you're using in Child1 in `componentWillReceiveProps` method. This way, whenever the `gridControl` is reset using the `onClick` function from Child2. Child1 will receive new props that you then will set to their corresponding states. which causes the component to rerender. Again, if you're using Redux, Flux, or any other store system, it will be easier for you to manipulate those states. – Mamdouh Alsarayreh Apr 27 '17 at 13:02
  • What would an example of `dataFromChild2` be? I really don't get the dataflow here, sorry, i must be misunderstanding something. I get that you want to do `this.props.controls = this.state.controls` in Child2, though. – cbll Apr 27 '17 at 13:10
1

If your structure looks something as the following:

<ParentComponent>
    <div>
        <Child1 />     
        <Child2 />
    </div>
<ParentComponent />

Both Child1 and Child2 should "communicate" through ParentComponent.
if Child2 will notify ParentComponent about a button click event then it can re-render Child1 with appropiate props accordingly or fire a function that Child1 gets as a prop. this is a basic flow in react.js

As an example consider a House component that has a Button and a Door child components. the Button will toggle opening and closing of the Door.

class House extends Component {

    constructor(props) {
        super(props);

        this.state = {
            isOpened: props.isOpened,
        };

        this.toggleOpenDoor = this.toggleOpenDoor.bind(this);
    }

    toggleOpenDoor() {
        this.setState({
            isOpened: !this.state.isOpened
        });
    }

    render() {
        return (
            <div>
                <Button onClick={this.toggleOpenDoor} />
                <Door isOpened={this.state.isOpened} />
            </div >
        );
    }
}

House.propTypes = {
    isOpened: React.PropTypes.bool
};

House.defaultProps = {
    isOpened: true
};

export default House;

On each change of this.state.isOpened the Door will re-render with the new value as prop

Sagiv b.g
  • 30,379
  • 9
  • 68
  • 99
0

METHOD 1: For this you need you need to maintain a store. On clicking button in your <Child2 /> component update the variable in the store. Read the updated variable in the store and if it has change update values in your <Child1 /> component. You can user either flux, redux, mobx etc.. as the store choices, but I would say you can start with redux.

METHOD2:

If you don't want to use store, keep a state in your <Parent /> and on button click in <Child2 /> update your state in parent through a callback function. Pass this state value as props to <Child1 /> and make changes if the prop is present.

Harkirat Saluja
  • 7,768
  • 5
  • 47
  • 73
  • It seems a bit overkill for a single page application(although a lot of the components present data), but I guess that's an option. – cbll Apr 27 '17 at 12:17