Xie Xie Fang
Xie Xie Fang

Reputation: 195

get form value from nested children using react.js

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

Upvotes: 4

Views: 971

Answers (2)

Sagiv b.g
Sagiv b.g

Reputation: 31024

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>

Upvotes: 1

Alexandre Annic
Alexandre Annic

Reputation: 10738

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);
    }
}

Upvotes: 4

Related Questions