4

How to pass child's state back to the container? I have a very simple app but because I split them into smaller component now I'm stuck passing state value (Form) back to its container. I can also call the api in child's component then recieve it via props container but that's not clean, I want the 'final event' be placed in the container, and the container has the 'main' state.

Is my first intention (make sure 'main' state is store in the container) correct? If yes how to pass back the state from child component back to the container? Assume I'm not using redux.

https://codesandbox.io/s/8zrjjz3yjj

2 Answers 2

4

Pass a method as props:

Parent:

class Parent extends Component {
    ....

    render = () => <Child onChange={childDidSomething}/>

    childDidSomething = state => {
        ...
    }
}

Child:

class Child extends Component {
    ....

    render() {...}

    somethingChanged() {
        this.props.onChange(this.state);
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

This is better. So that state returns a whole new object!
You can always repeat this process but it will quickly become stodgy. This is why React usually works with a state container like Redux.
1

You should lift the state up.

Let a parent component deal with a state that concern its children, and pass handlers to the children so the children can invoke them and pass them back the relevant values.

Here is a running example of this approach with your code:

class AddUser extends React.Component {

  onChange = ({ target }) => {
    const { onChange } = this.props;
    onChange(target.value);
  }

  render() {
    return (
      <div>
        <input onChange={this.onChange}
          type='text' placeholder='user' />
      </div>
    )
  }
}

class Modal extends React.Component {

  state = { newUser: '' }

  onUserChange = newUser => this.setState({ newUser });

  addUser = () => {
    const { addUser } = this.props;
    const { newUser } = this.state;
    addUser(newUser);
    this.setState({ newUser: '' }); // reset the field
  }

  render() {
    return (
      <div>
        <AddUser onChange={this.onUserChange} />
        <button onClick={this.addUser}>add</button>
      </div>
    )
  }
}

class MainContainer extends React.Component {

  state = {
    showAddUser: false,
    users: [{
      name: 'Jane'
    }, {
      name: 'Black'
    }]
  }

  addUserIntoUsers = (userName) => {
    const { users } = this.state;
    if (userName) { // only if we have a value
      const nextUsers = [...users, { name: userName }];
      this.setState({ users: nextUsers, showAddUser: false });
    }
  }

  render() {
    return (
      <div>
        <button onClick={() => this.setState({ showAddUser: !this.state.showAddUser })}>
          Toggle User Panel
        </button>
        <br />
        {this.state.users.map(o => {
          return (<div>{o.name}<br /></div>)
        })}
        {this.state.showAddUser && <Modal addUser={this.addUserIntoUsers} />}
      </div>
    )
  }
}

ReactDOM.render(<MainContainer />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

2 Comments

this is to bind the onChange, what if I want to get the value only? binding simple stuff is fine, but if the child has complicated state it will be hard to read if u bind it in the parent.
You can pass up whatever object you want, including the entire state. But keep in mind that this approach may lead you to an anti pattern. You should keep a single source of truth. Having a state of the same value both in the parent and child is not advised.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.