Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - Can A Child Component Send Value Back To Parent Form

The InputField & Button are custom components that go into a form to create a form. My issue is how do I send the data back up to form so that on button click, I can fire ajax on the form with data (username & password):

export default auth.authApi(   class SignUpViaEmail extends Component{      constructor(props){       super(props);       this.state = {         email : "",         password : ""       };       this.storeEmail = this.storeEmail.bind( this );       this.storePassword = this.storePassword.bind( this );     }      storeEmail(e){       this.setState({ email : e.target.value });     }      storePassword(e){       this.setState({ password : e.target.value });     }      handleSignUp(){       this.props.handleSignUp(this.state);     }      render(){       return(         <div className="pageContainer">            <form action="" method="post">             <InputField labelClass = "label"                         labelText = "Username"                         inputId = "signUp_username"                         inputType = "email"                         inputPlaceholder = "registered email"                         inputClass = "input" />             <Button btnClass = "btnClass"                     btnLabel = "Submit"                     onClickEvent = { handleSignUp } />           </form>         </div>       );     }    } ); 

Or Is it not recommended & I should not create custom child components within the form?

child component => InputField

import React,        { Component } from "react";  export class InputField extends Component{    constructor( props ){     super( props );     this.state = {       value : ""     };     this.onUserInput = this.onUserInput.bind( this );   }    onUserInput( e ){     this.setState({ value : e.target.value });     this.props.storeInParentState({[ this.props.inputType ] : e.target.value });   }    render(){     return  <div className = "">               <label htmlFor = {this.props.inputId}                      className = {this.props.labelClass}>                 {this.props.labelText}               </label>               <input id = {this.props.inputId}                      type = {this.props.inputType}                      onChange = {this.onUserInput} />               <span className = {this.props.validationClass}>                 { this.props.validationNotice }               </span>             </div>;   } } 

Error : I get the error e.target is undefined on the parent storeEmail func.

like image 338
Kayote Avatar asked Jan 12 '17 17:01

Kayote


2 Answers

React's one-way data-binding model means that child components cannot send back values to parent components unless explicitly allowed to do so. The React way of doing this is to pass down a callback to the child component (see Facebook's "Forms" guide).

class Parent extends Component {   constructor() {     this.state = {       value: ''     };   }      //...      handleChangeValue = event => this.setState({value: event.target.value});      //...      render() {     return (       <Child         value={this.state.value}         onChangeValue={this.handleChangeValue}       />     );   } }  class Child extends Component {   //...      render() {     return (       <input         type="text"         value={this.props.value}         onChange={this.props.onChangeValue}       />     );   } } 

Take note that the parent component handles the state, while the child component only handles displaying. Facebook's "Lifting State Up" guide is a good resource for learning how to do this.

This way, all data lives within the parent component (in state), and child components are only given a way to update that data (callbacks passed down as props). Now your problem is resolved: your parent component has access to all the data it needs (since the data is stored in state), but your child components are in charge of binding the data to their own individual elements, such as <input> tags.


Addendum

In response to this comment:

What if we render a list of the child component? Using this single source of truth in Lifting state up technique will let the parent controls all the state of all the child inputs right? So how can we access each of the value input in the child component to (which is rendered as list) from the parent component?

For this case, you may map a child component for each element in the list. For example:

class Parent extends Component {   //...   handleChangeListValue = index => event => {     this.setState({       list: this.state.list         .map((element, i) => i === index ? event.target.value : element)     });   }   //...   render() {     return this.state.list.map((element, i) => (       <Child         value={element}         onChangeValue={this.handleChangeListValue(i)}       />     )); 

P.S. Disclaimer: above code examples are only for illustrative purposes of the concept in question (Lifting State Up), and reflect the state of React code at the time of answering. Other questions about the code such as immutable vs mutable array updates, static vs dynamically generated functions, stateful vs pure components, and class-based vs hooks-based stateful components are better off asked as a separate question altogether.

like image 75
Ian Emnace Avatar answered Oct 05 '22 11:10

Ian Emnace


Parent.js

import React, { Component } from 'react'; import Child from './child' class Parent extends Component {   state = {     value: ''   }   onChangeValueHandler = (val) => {     this.setState({ value: val.target.value })   }   render() {     const { value } = this.state;     return (       <div>         <p> the value is : {value} </p>         <Child value={value} onChangeValue={this.onChangeValueHandler} />       </div>     );   } }  export default Parent; 

Child.js

  import React, { Component } from 'react';   class Child extends Component {   render() {   const { value , onChangeValue } = this.props;   return (     <div>       <input type="text"  value={value} onChange={onChangeValue}/>      </div>   ); } }   export default Child; 

you can see a live example on : https://codesandbox.io/s/two-way-binding-qq1o1?from-embed

like image 39
Ali Mohammad Avatar answered Oct 05 '22 12:10

Ali Mohammad