Parent inform children - reactjs

I've got a list (parent) with lots of items (children). There's a huge save button on the list. When the user presses that button, I'd like to have all the childrens save() method to be fired.
Usually, I'd use a custom event for this (or some PubSub implementation), but I wonder: What's the React.js way to achieve this?

The parent should have the state and render children with props. This is just a example, not tested.
Parent:
var Parent = React.createClass({
getInitialState: function(){
return {array: []}
},
handleChange: function(key, value){
// here will you get key and value from child input.
this.setState({array[key]: value});
},
saveAll: function(){
// this.state.array have all data, lets save it :)
},
render: function(){
var self = this;
var items = [];
this.state.array.map(function(item, key){
items.push(<Child handleChange={self.handleChange} index={key} value={item} />);
});
return (<div>{items} <button onClick={this.saveAll}>SaveAll</button> </div>
}
});
Child:
var Child = React.createClass({
onChange: function(event){
this.props.handleChange(this.props.index, event.target.value);
},
render: function(){
return (<li><input value={this.props.value} onChange={this.onChange} />)
}
});

Related

React js attach click event on every single span element

I have the following react code at: http://codepen.io/AlexanderWeb00/pen/ZOWyNr
I am trying to attach for every span a click event that allows to console.log the correspondent number that you have clicked on:
var one = [],
two = [];
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: one,
val2: two,
action: ''
}
},
handleClick: function() {
var val1 = this.state.val1,
val2 = this.state.val2;
if(!$('.action').length){
val1.push(this.props.numbers);
console.log(val1[0].join(" "));
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
var ff =this.handleClick;
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={ff} className={classes}>{el}</span>);
});
return (
<div className="row">
{numbers}
</div>
);
}
});
what I am getting instead is 123456789 as opposed to 5 if I only click on number 5.
As stated in the comment, you are pushing the whole numbers arrays into val1, therefore it will always display the whole list. To solve it we need to pinpoint the clicked span, get its number, and add it to the list.
The first method would be using jQuery, and the eventArg that the onClick sends.
Once the span is clicked our handleClick is called, with the click event data as a parameter. We will extract the clicked span from the event arg, ie eventArg.target, and get its text using jQuery (ie $(eventArg.target).text()).
To specifically solve the issue you could:
var one = [],
two = [];
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: one,
val2: two,
action: ''
}
},
handleClick: function(e) {
var val1 = this.state.val1,
val2 = this.state.val2;
if(!$('.action').length){
val1.push($(e.target).text());
console.log(val1.join(" "));
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
var ff =this.handleClick;
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={ff} className={classes}>{el}</span>);
});
return (
<div className="row">
{numbers}
</div>
);
}
});
https://jsfiddle.net/omerts/zm0bzmwp/
Another option is to wrap the span's number in a colsure. In other words, create an anonymous function, which will have the number as part of its scope object. Since closures are out of the scope of this question, for further reading: Closures
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: [],
val2: [],
action: ''
}
},
handleClick: function(number) {
if(!$('.action').length){
const updatedList = this.state.val1.slice(); // clone the array, and add the new number
updatedList.push(number);
console.log(updatedList.join(" "));
this.setState({val1: updatedList});
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={() => {this.handleClick(el)}} className={classes}>{el}</span>);
}, this);
return (
<div className="row">
{numbers}
</div>
);
}
});
https://jsfiddle.net/omerts/590my903/
Second option is better, since you should not be mutating state, unless you are using this.setState. If you don't really need it as part of your state, just make val1, and val2 data memebers of your SingleButton.

Event is not binding while creating dynamic controls

When I add an event using the first code snippet, the event is firing. But when i do the same thing with a variable, the event is not firing/binding. can any one help me?
var ProductTable = React.createClass({
ChangeSearch : function(event){console.log("Text changed");},
render: function() {
return (<input type="text"onChange= {this.ChangeSearch} />);
}
});
same code with variable:
var ProductTable = React.createClass({
var headerFilters =[];
ChangeSearch : function(event){console.log("Text changed");},
render: function() {
headerFilters.push(<th><input type="text" onChange={this.ChangeSearch} /></th>);
return ({headerFilters});
}
});
First one and the second one are looping through and adding the text boxes. With the variable only i will be able to generalize the code. I have removed the looping from the first code snippet to reduce the complexity.
If you need to render list of input elements its better to create simple element and map over it in Parent. Read react documentation about dynamic children
jssfidle:
code sample:
var InputTextElement = React.createClass({
changeHandler: function(event) {
console.log('text.changed' + event.target.value)
},
render: function() {
return (
<input type="text" name={this.props.name} onChange={this.changeHandler} />
)
}
})
var ProductTable = React.createClass({
render: function() {
var headerFilters =[{id: 1, name: "test"}, {id:2, name: "another"}];
return (
<div>
{ headerFilters.map(function(data) {
return <InputTextElement key={data.id} name={data.name} />;
})}
</div>
);
}
});
setTimeout(function() {
ReactDOM.render( <ProductTable />
, document.getElementById('element'))
} ,700);
working like a charm.

How to listen for DOM updates in a react.js component?

I am trying to figure out why a react.js component is not updating when I call this.setState() from it. I would like to try to listen for all DOM updates as a way of debugging this. Is this possible?
var ChangingProgressBar = React.createClass({
getInitialState: function(){
return {'percentage': 0,
'mood':'progress-bar-info'}
},
componentWillMount: function(){
self = this;
setTimeout(function(){
self.setState({'mood', 'progress-bar-danger'});
}, 2000);
},
render: function(){
return (
<div className='progress-bar {this.state.mood}'
role='progressbar' aria-valuenow='{this.state.percentage}'
aria-valuemin='0' aria-valuemax='100'
style="{{width: this.state.percentage + '%'}}">
{this.state.percentage}%
</div>
)
})
})
self.setState({'mood', 'progress-bar-danger'});
should be
self.setState({'mood': 'progress-bar-danger'});

How to Render Component Dynamically in React

I have developed a Component base application in which I have developed a different Component but at time of rendering I have to render based on user action currently what I have tried is mentioned below.
My Component looks like:
var React = require('react');
var Constants = require('../constants/ActionTypes');
var ItemRelationStore = require('../stores/ItemRelationStore');
var ItemStore=require('../stores/ItemStore');
var TreeView=require('./TreeView.react');
var childNodes={};
function getItemRelationState() {
return {
Items:JSON.parse(JSON.stringify(ItemStore.getItem())),
Items1:JSON.parse(JSON.stringify(ItemStore.getItem1()))
};
}
// Define main Controller View
var ItemPage = React.createClass({
// Get initial state from stores
getInitialState: function() {
return getItemRelationState();
},
// Add change listeners to stores
componentDidMount: function() {
ItemStore.CallItem("TenantId",this.props.MetaItemId,this.props.Key);
ItemStore.addChangeListener(this._onChange);
},
// Remove change listers from stores
componentWillUnmount: function() {
ItemStore.removeChangeListener(this._onChange);
},
// Render our child components, passing state via props
render: function() {
var itemslist;
if(this.props.Key==1)
{
itemslist=this.state.Items;
}
else
{
itemslist=this.state.Items1;
}
var metaid=this.props.MetaItemId;
childNodes= Object.keys(itemslist).map(function(item,index){
return (
{
title:itemslist[item].Name,
Id: itemslist[item].Id,
metaitemId:metaid
}
)
});
var tree= {
title: this.props.MetaItemName,
metaitemId:metaid,
childNodes: [
{childNodes}
]
};
return (
<div className="treeview">
<TreeView node={tree}/>
</div>
);
},
// Method to setState based upon Store changes
_onChange: function() {
this.setState(getItemRelationState());
}
});
module.exports = ItemPage;
Now when I used static in another component like
var Item=require('../../Item');
React.render(<Item MetaItemName={"Users"} MetaItemId={1} Key={1}/>,document.getElementById("firstCol"));
then it's working fine. but I want to render dynamically so I have prepared one CustomeComponent like
var React = require('react');
var Item=require('../Item');
var CustomComponent = React.createClass({
// Get initial state from stores
getInitialState: function() {
return null;
},
// Add change listeners to stores
componentDidMount: function() {
},
componentWillReceiveProps: function(nextProps) {
// Load new data when the dataSource property changes.
if (nextProps.dataSource != this.props.dataSource) {
this.loadData(nextProps.dataSource);
}
},
// Remove change listers from stores
componentWillUnmount: function() {
},
// Render our child components, passing state via props
render: function() {
var InputType=this.props.inputType;
console.log(InputType);
return (
<InputType/>
);
},
// Method to setState based upon Store changes
_onChange: function() {
}
});
module.exports = CustomComponent;
and I am calling this custom component like
React.render(<CustomComponent inputType="Item" />,document.getElementById("secondCol"));
but it's not working i mean to say nothing render on Page.
I just want to know how we can Achive dynamic rendering in React.
Thanks in Advance.
Maybe React.render(<CustomComponent inputType={Item} />,document.getElementById("secondCol"));?

ReactJS can't access "this" methods

I am trying to pass a method to a child component to handle onclick events.
I saw a lot of examples online, but I can't get it working.
When I am inside the render function of the parent and trying to pass "this.handleClick" to the child, handleClick is undefined.
Have a look at render method of ThumbList:
var Thumb = React.createClass({
handleClick: function() {
console.log(this)
console.log('handleClick of Thumb')
this.props.onClick()
},
render: function() {
return(
<div className="thumb" key={this.props.thumb.url}>
<a href='#' onClick={this.handleClick}>
<img src={'/img/rings/thumbs/'+this.props.thumb.url+'_thumb.jpg'} alt="Image">
</img>
</a>
</div>
);
}
});
var ThumbList = React.createClass({
handleClick: function (id) {
console.log('click of ThumbList');
},
loadFromServer: function() {
$.ajax({
url: 'rings/imgs/5',
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('rings/imgs/5', status, err.toString());
}.bind(this)
});
},
getInitialState: function(){
return {data: [] };
},
componentDidMount: function(){
this.loadFromServer();
setInterval(this.loadFromServer, 2000);
},
render: function() {
var handlefunc=this.handleClick
var thumbsNodes = this.state.data.map(function(thumb) {
console.log(this.handleClick) // is Undefined!
console.log(handlefunc) // is working
return (
<Thumb thumb={thumb} key={thumb.url} onClick={handlefunc.bind(this,thumb.url)}/>
);
});
return(
<div className="col-md-1 col-md-offset-1" id='thumbs'>
{thumbsNodes}
</div>
);
}
});
Any idea what I might be missing?
If you're using a compiler like Babel as part of your development workflow, I'd suggest using arrow functions:
var thumbsNodes = this.state.data.map((thumb) => {
console.log(this.handleClick);
return <Thumb thumb={thumb} key={thumb.url}
onClick={this.handlefunc.bind(this,thumb.url)}/>;
});
As you can see, it's a nice compact syntax. The arrow function will preserve the this context for you. The Babel compiler produces JavaScript that uses a closure:
var thumbsNodes = this.state.data.map(function(thumb) {
var _this = this;
console.log(_this.handleClick);
return <Thumb thumb={thumb} key={thumb.url}
onClick={_this.handlefunc.bind(_this,thumb.url)}/>;
});
this is undefined because the map callback does not know what it is. The simplest way to solve this is to pass a second argument, and it will use that as this in the callback:
var thumbsNodes = this.state.data.map(function(thumb) {
console.log(this.handleClick)
return <Thumb thumb={thumb} key={thumb.url} onClick={handlefunc.bind(this,thumb.url)}/>
}, this)
More: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
You need to grab a reference to this so it is the correct context when you call it.
Try
render: function()
{
var handlefunc = this.handleClick; // i assume this is just for debugging the issue
var self = this;
var thumbsNodes = this.state.data.map(function(thumb)
{
console.log(self.handleClick) // note the use of `self`
});
}

Resources