[javascript] Pass props to parent component in React.js

Is there not a simple way to pass a child's props to its parent using events, in React.js?

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(event) {
    // event.component.props ?why is this not available?
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

I know you can use controlled components to pass an input's value but it'd be nice to pass the whole kit n' kaboodle. Sometimes the child component contains a set of information you'd rather not have to look up.

Perhaps there's a way to bind the component to the event?

UPDATE – 9/1/2015

After using React for over a year, and spurred on by Sebastien Lorber's answer, I've concluded passing child components as arguments to functions in parents is not in fact the React way, nor was it ever a good idea. I've switched the answer.

This question is related to javascript reactjs higher-order-components

The answer is


Update (9/1/15): The OP has made this question a bit of a moving target. It’s been updated again. So, I feel responsible to update my reply.

First, an answer to your provided example:

Yes, this is possible.

You can solve this by updating Child’s onClick to be this.props.onClick.bind(null, this):

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
  }
});

The event handler in your Parent can then access the component and event like so:

  onClick: function (component, event) {
    // console.log(component, event);
  },

JSBin snapshot


But the question itself is misleading

Parent already knows Child’s props.

This isn’t clear in the provided example because no props are actually being provided. This sample code might better support the question being asked:

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick}> {this.props.text} </a>;
  }
});

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (event) {
    // event.component.props ?why is this not available? 
  },
  render: function() {
    return <Child onClick={this.onClick} text={this.state.text} />;
  }
});

It becomes much clearer in this example that you already know what the props of Child are.

JSBin snapshot


If it’s truly about using a Child’s props…

If it’s truly about using a Child’s props, you can avoid any hookup with Child altogether.

JSX has a spread attributes API I often use on components like Child. It takes all the props and applies them to a component. Child would look like this:

var Child = React.createClass({
  render: function () {
    return <a {...this.props}> {this.props.text} </a>;
  }
});

Allowing you to use the values directly in the Parent:

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
  }
});

JSBin snapshot


And there's no additional configuration required as you hookup additional Child components

var Parent = React.createClass({
  getInitialState: function () {
    return {
      text: "Click here",
      text2: "No, Click here",
    };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <div>
      <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
      <Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
    </div>;
  }
});

JSBin snapshot

But I suspect that’s not your actual use case. So let’s dig further…


A robust practical example

The generic nature of the provided example is a hard to talk about. I’ve created a component that demonstrations a practical use for the question above, implemented in a very Reacty way:

DTServiceCalculator working example
DTServiceCalculator repo

This component is a simple service calculator. You provide it with a list of services (with names and prices) and it will calculate a total the selected prices.

Children are blissfully ignorant

ServiceItem is the child-component in this example. It doesn’t have many opinions about the outside world. It requires a few props, one of which is a function to be called when clicked.

<div onClick={this.props.handleClick.bind(this.props.index)} />

It does nothing but to call the provided handleClick callback with the provided index[source].

Parents are Children

DTServicesCalculator is the parent-component is this example. It’s also a child. Let’s look.

DTServiceCalculator creates a list of child-component (ServiceItems) and provides them with props [source]. It’s the parent-component of ServiceItem but it`s the child-component of the component passing it the list. It doesn't own the data. So it again delegates handling of the component to its parent-component source

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />

handleServiceItem captures the index, passed from the child, and provides it to its parent [source]

handleServiceClick (index) {
  this.props.onSelect(index);
}

Owners know everything

The concept of “Ownership” is an important one in React. I recommend reading more about it here.

In the example I’ve shown, I keep delegating handling of an event up the component tree until we get to the component that owns the state.

When we finally get there, we handle the state selection/deselection like so [source]:

handleSelect (index) {
  let services = […this.state.services];
  services[index].chosen = (services[index].chosen) ? false : true;
  this.setState({ services: services });
}


Conclusion

Try keeping your outer-most components as opaque as possible. Strive to make sure that they have very few preferences about how a parent-component might choose to implement them.

Keep aware of who owns the data you are manipulating. In most cases, you will need to delegate event handling up the tree to the component that owns that state.

Aside: The Flux pattern is a good way to reduce this type of necessary hookup in apps.


Here is a simple 3 step ES6 implementation using function binding in the parent constructor. This is the first way the official react tutorial recommends (there is also public class fields syntax not covered here). You can find all of this information here https://reactjs.org/docs/handling-events.html

Binding Parent Functions so Children Can Call Them (And pass data up to the parent! :D )

  1. Make sure in the parent constructor you bind the function you created in the parent
  2. Pass the bound function down to the child as a prop (No lambda because we are passing a ref to function)
  3. Call the bound function from a child event (Lambda! We're calling the function when the event is fired. If we don't do this the function will automatically run on load and not be triggered on the event.)

Parent Function

handleFilterApply(filterVals){} 

Parent Constructor

this.handleFilterApply = this.handleFilterApply.bind(this);

Prop Passed to Child

onApplyClick = {this.handleFilterApply}

Child Event Call

onClick = {() => {props.onApplyClick(filterVals)}

The question is how to pass argument from child to parent component. This example is easy to use and tested:

//Child component
class Child extends React.Component {
    render() {
        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
        )
    }
}

//Parent component
class Parent extends React.Component {
    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
    }

    handleToUpdate(someArg){
        alert('We pass argument from Child to Parent: \n' + someArg);
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;
        return (<div>
          <Child handleToUpdate = {handleToUpdate.bind(this)} />
        </div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <Parent />,
        document.querySelector("#demo")
    );
}

Look at JSFIDDLE


This is an example without using the onClick event. I simply pass a callback function to the child by props. With that callback the child call also send data back. I was inspired by the examples in the docs.

Small example (this is in a tsx files, so props and states must be declared fully, I deleted some logic out of the components, so it is less code).

*Update: Important is to bind this to the callback, otherwise the callback has the scope of the child and not the parent. Only problem: it is the "old" parent...

SymptomChoser is the parent:

interface SymptomChooserState {
  // true when a symptom was pressed can now add more detail
  isInDetailMode: boolean
  // since when user has this symptoms
  sinceDate: Date,
}

class SymptomChooser extends Component<{}, SymptomChooserState> {

  state = {
    isInDetailMode: false,
    sinceDate: new Date()
  }

  helloParent(symptom: Symptom) {
    console.log("This is parent of: ", symptom.props.name);
    // TODO enable detail mode
  }

  render() {
    return (
      <View>
        <Symptom name='Fieber' callback={this.helloParent.bind(this)} />
      </View>
    );
  }
}

Symptom is the child (in the props of the child I declared the callback function, in the function selectedSymptom the callback is called):

interface SymptomProps {
  // name of the symptom
  name: string,
  // callback to notify SymptomChooser about selected Symptom.
  callback: (symptom: Symptom) => void
}

class Symptom extends Component<SymptomProps, SymptomState>{

  state = {
    isSelected: false,
    severity: 0
  }

  selectedSymptom() {
    this.setState({ isSelected: true });
    this.props.callback(this);
  }

  render() {
    return (
      // symptom is not selected
      <Button
        style={[AppStyle.button]}
        onPress={this.selectedSymptom.bind(this)}>
        <Text style={[AppStyle.textButton]}>{this.props.name}</Text>
      </Button>
    );
  }
}

Basically you use props to send information to and from Child and Parent.

Adding to all the wonderful answers, let me give a simple example that explains passing values from child to parent component in React

App.js

class App extends React.Component {
      constructor(){
            super();
            this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
            this.state={name:'igi'}
      }
      handleFilterUpdate(filterValue) {
            this.setState({
                  name: filterValue
            });
      }
   render() {
      return (
        <div>
            <Header change={this.handleFilterUpdate} name={this.state.name} />
            <p>{this.state.name}</p>
        </div>
      );
   }
}

Header.js

class Header extends React.Component {
      constructor(){
            super();
            this.state={
                  names: 'jessy'
            }
      }
      Change(event) {

      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

   render() {
      return (
       <button onClick={this.Change.bind(this)}>click</button>

      );
   }
}

Main.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('app'));

Thats it , now you can pass values from your client to the server.

Take a look at the Change function in the Header.js

Change(event) {
      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

This is how you push values into the props from client to the server


It appears there's a simple answer. Consider this:

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick.bind(null, this)}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(component, event) {
    component.props // #=> {Object...}
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

The key is calling bind(null, this) on the this.props.onClick event, passed from the parent. Now, the onClick function accepts arguments component, AND event. I think that's the best of all worlds.

UPDATE: 9/1/2015

This was a bad idea: letting child implementation details leak in to the parent was never a good path. See Sebastien Lorber's answer.


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to reactjs

Error: Node Sass version 5.0.0 is incompatible with ^4.0.0 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app Template not provided using create-react-app How to resolve the error on 'react-native start' Element implicitly has an 'any' type because expression of type 'string' can't be used to index Invalid hook call. Hooks can only be called inside of the body of a function component How to style components using makeStyles and still have lifecycle methods in Material UI? React Hook "useState" is called in function "app" which is neither a React function component or a custom React Hook function How to fix missing dependency warning when using useEffect React Hook? Unable to load script.Make sure you are either running a Metro server or that your bundle 'index.android.bundle' is packaged correctly for release

Examples related to higher-order-components

Functions are not valid as a React child. This may happen if you return a Component instead of from render How do I conditionally add attributes to React components? Delayed rendering of React components Can you force a React component to rerender without calling setState? Hide/Show components in react native How to print React component on click of a button? React / JSX Dynamic Component Name Update style of a component onScroll in React.js react-router - pass props to handler component React component not re-rendering on state change