Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Values via Props From React Stateless Child to Parent

CONTEXT

I'm trying to pass input value fields (conditionTitle) from a React Stateless child component (AddConditionSelect) to the parent component (AddConditionDashboard) that will hold my state.

PROBLEM

I followed the model shown in the React documentation, but they are using refs, which only works if the component is stateful. I do not want to have to set any state in the child component, but still be able to access the input in the parent.

In its current form, I am getting a Warning, that the stateless function components cannot be given refs, resulting in props being null and undefined.

Parent Component:

import AddConditionSelect from '../containers/AddConditionSelect.js';

class AddConditionDashboard extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      conditionTitle: '',
      conditionType: ''
    };
  }

  handleUserInput({conditionTitleInput}) {
    this.setState({
      conditionTitle:conditionTitle
    })

  }

  render() {
    const {error, segmentId} = this.props;

    return (
      <div>

    <AddConditionSelect segmentId={segmentId} conditionTitle={this.state.conditionTitle} onUserInput={this.handleUserInput} />


    <PanelFooter theme="default">
      <Button backgroundColor="primary" color="white" inverted={true} rounded={true} onClick={(event) => this.onSubmit(event)}>
        Next Step
      </Button>
    </PanelFooter>

      </div>
    );
  }

}

export default AddConditionDashboard;

Child component:

class AddConditionSelect extends React.Component {

  onInputChange: function() {
    this.props.onUserInput(
      this.refs.conditionTitleInput.value,
    )
  },

  render() {
    const {error} = this.props;

    return (
      <div>

        <Panel theme="info">

        <Divider />

        Please enter a name {error ? <Message inverted={true}  rounded={true}  theme="error">{error}</Message>  : null}
          <Input value={this.props.conditionTitle} ref="conditionTitleInput" label="" type="text" buttonLabel="Add Condition" name="add_segment" onChange={this.onInputChange} placeholder="Condition Title"/>

       </Panel>
     </div>
    );
  }

}
export default AddConditionSelect;
like image 425
Dan Avatar asked May 31 '16 20:05

Dan


2 Answers

How about passing the event handler directly to <Input>? This way you pass the on change event directly to your parent (grandparent of <Input>) and you can extract the value from event.target.value so no need to use refs:

Note: You might have to bind the context of onUserInputChange() in you parent's constructor because event handlers have the element on which the event happened as their context by default:

Parent

class AddConditionDashboard extends React.Component {

  constructor(props) {
    // ...

    // bind the context for the user input event handler
    // so we can use `this` to reference `AddConditionDashboard`
    this.onUserInputChange = this.onUserInputChange.bind(this);
  }

  onUserInputChange({ target }) {
    const { value: conditionTitle } = target;
    this.setState({
     conditionTitle
    });
  }

  render() {
    // ...

    <AddConditionSelect segmentId={segmentId} 
                        conditionTitle={this.state.conditionTitle} 
                        onUserInputChange={this.onUserInputChange} // <-- pass event handler to child that will pass it on to <Input>
    />

    // ...
  }
  // ...

Child:

class AddConditionSelect extends React.Component {

  render() {
    const { error } = this.props;

    return (
      <div>
        // ...

        <Input value={this.props.conditionTitle} 
               label="" 
               type="text" 
               buttonLabel="Add Condition" 
               name="add_segment" 
               onChange={this.props.onUserInputChange} // <-- Use the grandparent event handler
               placeholder="Condition Title"
        />

       // ...
     </div>
    );
  }
}
like image 60
nem035 Avatar answered Nov 14 '22 22:11

nem035


  1. You don't need ref. value can be extracted from the event. Thus, you children component becomes simpler.
  2. You can simplify your code further using the value link pattern.

    class AddConditionDashboard extends React.Component { constructor(props) { super(props);

    this.state = {
      conditionTitle: '',
      conditionType: ''
    };
    

    }

    render() { const {error, segmentId} = this.props;

    return (
      <div>
    
        <AddConditionSelect segmentId={segmentId} conditionTitle={ Link.state( this, 'conditionTitle' ) } />
    
    
        <PanelFooter theme="default">
        <Button backgroundColor="primary" color="white" inverted={true} rounded={true} onClick={(event) => this.onSubmit(event)}>
            Next Step
        </Button>
        </PanelFooter>
    
      </div>
    );
    

    }

    }

    export default AddConditionDashboard;

And child component

const AddConditionSelect = ({ error, valueLink }) => (
    <div>

    <Panel theme="info">

    <Divider />

    Please enter a name { error ?
                            <Message inverted={true}  rounded={true}  theme="error">
                                {error}
                            </Message>  : null }
        <Input  value={ valueLink.value }
                onChange={ e => valueLink.set( e.target.value ) }
                label="" type="text"
                buttonLabel="Add Condition" name="add_segment" 
                placeholder="Condition Title"/>

    </Panel>
    </div>
);

export default AddConditionSelect;
like image 31
gaperton Avatar answered Nov 14 '22 23:11

gaperton