I have a react.js app with structure like this:
<CalcApp /> --- root component, state is set here
<CalcTable /> --- 1st child component
<CalcRow /> --- 2nd child component
- input onChange event
I want to be able to listen to an an onChange event happening inside the <-CalcRow-> component, but I am having trouble passing the event all the way up to the root component.
I have looked around the web, and stackoverflow, pretty extensively, but have not found examples of how to do this.
What is the best way to pass the event from a deeply nested child component all the way back up to the root component so that I can change state?
Here is my complete code:
var React = window.React = require('react'),
// include external components from ui folder like the exmaple blow:
// Timer = require("./ui/Timer"),
Timer = require("./ui/Timer"),
mountNode = document.getElementById("app");
var catOne = [
{name : 'one', value : 5, key : 1},
{name : 'two', value : 2, key : 2},
{name : 'three', value : 3, key : 3}
];
var CalcTable = React.createClass({
changeHandler: function(){
console.log('ding');
this.props.onChange();
},
render: function() {
var rows = [];
// var myVar = this.props.cat1;
this.props.cat1.forEach(function(item){
rows.push(<CalcRow item={item} ref="row" key={item.key} onChange={this.changeHandler} />);
});
return(
<table>{rows}</table>
)
}
});
var CalcRow = React.createClass({
changeHandler: function(e) {
console.log('ping');
this.props.onChange();
},
render: function(){
return(
<tr>
<td>h</td>
<td>{this.props.item.name}</td>
<td><input cat1={this.props.item} value={this.props.item.value} name={this.props.item.key} onChange={this.changeHandler} /></td>
</tr>
)
}
});
var AddRowButton = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
this.props.onSubmit(this);
},
render: function(){
return(
<form onSubmit={this.handleSubmit}>
<input />
<button>Add</button>
</form>
)
}
});
var SectionSummary = React.createClass({
render: function(){
return(
<div className="summary">
<div className="table-summary">
stuff
</div>
</div>
);
}
});
var CalcApp = React.createClass({
changeHandler: function(e) {
console.log('bong');
},
getInitialState: function(){
return {
cat1: this.props.cat1
};
},
handleSubmit: function() {
// console.log(this.props.cat1);
// console.log(this.props.cat1.length+1);
var newKeyVal = this.props.cat1.length+1;
c = this.props.cat1;
c = c.push({name : "four", value : 4, key : newKeyVal});
this.setState({
cat1:c
});
// console.log(this.state.cat1);
},
render: function() {
return (
<div>
<h3>title</h3>
<CalcTable cat1={this.props.cat1} onChange={this.changeHandler}/>
<div className="stuff"><p>stuff</p></div>
<div className="stuff">
<AddRowButton cat1={this.props.cat1} onSubmit={this.handleSubmit}/>
</div>
<SectionSummary />
</div>
);
}
});
React.render(<CalcApp cat1={catOne}/>, mountNode);
First, you'll need to create two components, one parent and one child. Next, you'll import the child component in the parent component and return it. Then you'll create a function and a button to trigger that function. Also, you'll create a state using the useState Hook to manage the data.
To move data from a component to a sibling component, the simplest way is to pass a function from the parent component as a prop (i.e. "prop function") to its child that wants to update the data.
Answer. By passing an anonymous function, or a named function, with multiple event handler calls as the function body, to our event listener (like onClick , onKeyUp , onChange , etc) we can call multiple event handlers in response to a single event.
Just a word of caution when using pub/sub: It's too easy to go overboard with events. At first, it seems to solve all problems you've ever had. Then after a while you notice that all of your code is a tangled ball of event-spaghetti.
Events are a bit like globals in that they tend to make everything better until they make everything worse. And when you end up where they make everything worse, it's hard to go back unfortunately.
So use events sparsely. Prefer passing down functions to child components so that child components can notify parents. That too is a sort of event, but more isolated and easier to follow.
You might think "Well what about Flux, that is all about events?". And it sure it, but has quite a rigid structure for how and when to send events which makes it easier to manage.
One solution can be to use a common event dispatcher.
In your parent component, you would write
var dispatcher = require('./my-dispatcher')
dispatcher.on('my.event', function (e, myArg) {
// do some stuff here
})
and in the child
var dispatcher = require('./my-dispatcher')
dispatcher.trigger('my.event', 'my arg value')
If you do not want to implement the dispatcher yourself, there are some libraries around. I have not tried it, but for example https://github.com/mrdoob/eventdispatcher.js/ seems to do this job.
I use PubSubJS to communicate between my React components... that way you keep everything independent of each other and loosely coupled.
You can read more about PubSubJS here: https://github.com/mroderick/PubSubJS
To communicate, one component publish and any subscriber will get the data.
I learned about this from this blog: http://maketea.co.uk/2014/03/05/building-robust-web-apps-with-react-part-1.html#component-communication
He is using window.addEventListener
but I don't recommend it since not all browser supports it, plus the PubSubJS is much easier to implement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With