React JS: Explanation of this.props.items.map feature - javascript

I am using React JS for Rendering the HTML content. The issue is I am not able to understand particular section of code what it does.
If you can see a basic sample of a Todo List from the below link
http://facebook.github.io/react/
<script type='text/jsx'>
/** #jsx React.DOM */
var TodoList = React.createClass({
render: function(){
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function(){
return {items:[], text: ''}
},
onChange: function(e)
{
this.setState({text: e.target.value});
},
handleSubmit: function(e)
{
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = ''
this.setState({items: nextItems, text: nextText});
},
render:function(){
return (
<div>
<h3>ToDo List</h3>
<TodoList items={this.state.items}/>
<form onSubmit={this.handleSubmit}>
<input type="text" onChange={this.onChange} value={this.state.text}/>
<button>Add #{this.state.items.length+1}</button>
</form>
</div>
)
}
});
React.render(<TodoApp />, document.getElementById('toDoListApp'));
</script>
I am basically not able to understand what map does and how create item parameters are working. Could anyone provide details on the same:
var TodoList = React.createClass({
render: function(){
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
Thanks,
Ankit

map is not a feature of React.js. You can call this function on any array you want. You should look at its documentation at MDN for that.
Basically, map is for converting an array to another array with modified items.
For example:
[1,2,3].map(function(item){
return item+1;
})
would return a new array like this: [2,3,4]
In your example, map is used to convert an array with items of type "string" to an array of React.DOM.li elements.
The autor of your example could also have done it like this
var TodoList = React.createClass({
render: function(){
return <ul>{this.createItems(this.props.items)}</ul>;
},
createItems: function(items){
var output = [];
for(var i = 0; i < items.length; i++) output.push(<li>{items[i]}</li>);
return output;
}
});

props is an object containing properties passed from a parent to a child component.
So props.items is the property named items which is an array.
props.item.map() maps the items arrary to an array of lis.

It will take this.props.items array, pass each item to the createItem function, and then return an array of the returned values of each call.
Specifically for that code, if you had this in this.props.items:
["Item 1 text", "Item 2 text", ..]
You'd get something like this from the map call:
["<li>Item 1 text</li>","<li>Item 2 text</li>",..]

this.props.items is an array and map return the new array according to callback function that provide as an first argument, it is quit easy to using ES6 and JSX.
<tr> { this.arr.map((obj, i) => <td key={i}> {obj.name} </td>) } </tr>
In our example it will return array of td's

Related

JSX skip if JSON field does not exist

I am currently working with a JSON file from an API that does not add a key pair if the field is null. This is causing me grief when trying to iterate through the entire JSON file.
My code currently is
var ListOfItems = React.createClass({
render: function () {
var itemList = jsonFile.data.map(function(item)
{
return <Item key={item.__key}
itemQuestion={item.question}
itemQuestionAnswer={item.answer.answer}
userName={item.user.name}
staffName={item.staff.name}
staffImage={item.staff.image_url} />
});
return (
<div>
<ul>{itemList}</ul>
</div>
);
}
});
Which gives an error for when item.answer.answer has no value.
Any help will be greatly appreciated.
You can do that by adding a condition to check, whether the item's answer type is not undefined. If it's not, proceed with returning a value, else don't return anything. This way, you only append another item if the condition has passed (I used shorthand for the condition).
var ListOfItems = React.createClass({
render: function() {
var itemList = jsonFile.data.map(function(item)
{
typoeof item.answer != 'undefined' &&
return <Item key={item.__key} itemQuestion={item.question}
itemQuestionAnswer={item.answer.answer} userName={item.user.name}
staffName={item.staff.name} staffImage={item.staff.image_url} />
});
return (
<div>
<ul>{itemList}</ul>
</div>
);
}
});
If you always get item.answer but it's answer is either undefined or null, you can check for item.answer.answer in the code I've provided instead.
Depending on how big your list of items is, you could use the builtin filter Array method to first remove all the items you don't want, and then proceed to map through them. Keep in mind this will potentially go through your entire list twice.
A note about returning undefined from inside map. This will not prevent an item from getting returned. You will instead have an undefined item in your resulting array. The array will not be shorter.
Here's an example with filter():
var ListOfItems = React.createClass({
renderItems: function() {
return jsonFile.data
.filter(function(item) {
// This will return any item that has a truthy answer
return item.answer && item.answer.answer;
})
.map(function(item) {
return (
<Item
key={item.__key}
itemQuestion={item.question}
itemQuestionAnswer={item.answer.answer}
userName={item.user.name}
staffName={item.staff.name}
staffImage={item.staff.image_url} />
);
});
},
render: function() {
return (
<div>
<ul>
{this.renderItems()}
</ul>
</div>
);
}
});

Passing parameter in ReactJS onclick

I have this React component that has items generated from a list (with a map function). Each of those elements has a button. I want this button's onclick button to pass in a parameter to identify which list item's button was clicked.
It looks something like this.
var Component = React.createClass({
assignItem: function(item){
this.setState({item:item})
},
render: function(){
var listItems = list.map(function(item){
return <div>{item}
<button onClick={this.assignItem(item)>Click</button>
</div>
})
return <div>{listItems}</div>
}
})
Of course that doesn't work. The error message says that this.assignItem is not a function.
I know the official React documentation suggests this:
var handleClick = function(i, props) {
console.log('You clicked: ' + props.items[i]);
}
function GroceryList(props) {
return (
<div>
{props.items.map(function(item, i) {
return (
<div onClick={handleClick.bind(this, i, props)} key={i}>{item}</div>
);
})}
</div>
);
}
ReactDOM.render(
<GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
);
but that works with a function outside of the component. Since I want my function to manipulate the sate, I want to keep it within the React component.
How do I do this?
Original accepted answer:
You can bind to a function on this just like the React example, however since you are rendering in a map callback you need to either pass in thisArg or use a fat arrow function:
var Component = React.createClass({
assignItem: function(item){
this.setState({item:item})
},
render: function(){
// bind to this.assignItem
var listItems = list.map(function(item){
return <div>{item}
<button onClick={this.assignItem.bind(this, item)}>Click</button>
</div>
}, this); // pass in this, or use fat arrow map callback
return <div>{listItems}</div>
}
})
Update 2017:
This is an old question using an old React API and an accordingly old answer. Today you should be using the class or functional React component API. For passing arguments to click handlers you can just write an inline fat arrow function and call through with whatever params you want. The above example ends up like this:
class MyComponent extends React.Component { // or React.PureComponent
assignItem = item => { // bound arrow function handler
this.setState({ item: item });
}
render() {
var listItems = list.map(item => {
// onClick is an arrow function that calls this.assignItem
return <div>{item}
<button onClick={e => this.assignItem(item)}>Click</button>
</div>
});
return <div>{ listItems }</div>
}
}
Note: The assignItem handler must be bound, which is done here using an arrow function as a class property.

How to append multiple DOM elements to an element created with reactjs?

I've just started work with reactjs and have not done much hands-on work with it. So far I'm able to create DOM elements through reactjs using JSXTransformer.js. The problem I'm getting is, when I try to create multiple elements within a DOM element, it replaces the old elements with the new ones.
That is, if I want to create div_B, div_C and div_D in mainDiv, it just adds div_D in the mainDiv because it is create last. But I want to append all three divs in the mainDiv.
The code I'm using is following:
var props = [];
function getEle(id) {
return document.getElementById(id);
}
function setProps(ele, Css, inner, id) {
props.element = ele;
props.CssClass = Css;
props.innerText = inner;
props.id = id;
return props;
}
function createElement(properties , element){
var CreateDiv = React.createClass({
render : function(){
return <div className = {this.props.elementProps.CssClass} id={this.props.elementProps.id}>{this.props.innerText}</div>;
}
});
React.render(<CreateDiv elementProps = {properties} />, element);
}
setProps("div", "divBClass", "", "div_B");
createElement(props, getEle("mainDiv"));
setProps("div", "divCClass", "", "div_C");
createElement(props, getEle("mainDiv"));
setProps("div", "divDClass", "", "div_D");
createElement(props, getEle("mainDiv"));
Is there anything wrong with that code?
You are still thinking about your code in an imperative manner. React is based on a declarative programming paradigm.
First, think about your whole application as a React component.
var App = React.createClass({
render: function() {
return (
<div>foo</div>
);
}
})
React.render(<App />, document.body);
Now, let's first render some paragraphs:
var App = React.createClass({
render: function() {
// construct array [0, 1, 2]
var values = [];
for (var i=0; i<this.props.noDivs; i++) {
values.push(i);
}
// return <p>0</p> <p>1</p> ...
return (
<div>
{values.map(function (value) {
return <p key={value}>Value {value}</p>;
})}
</div>
);
}
})
React.render(<App noDivs={3} />, document.body);
If JSX is too much, try to compile it to Javascript. Here is a live example. I'm passing the number of paragraphs as a prop.

Delete node from Firebase using React and firereact

I'm pretty puzzled by the way reactfire works, as there doesn't seem to much about it documentation-wise.
So I want to delete and update some child nodes, but I have no idea how to do it. All tutorials focus solely in retrieving data, which are treated as a regular array and I don't even seem to get access to their keys.
Here's the official example: https://www.firebase.com/blog/2014-05-01-using-firebase-with-react.html
How do perform these operations using React?
Once you instantiate FB:
```
this.fb = new Firebase('https://react-testing.firebaseio.com/items');
this.bindAsArray(this.fb, 'items');
```
'items' becomes bound to this.state.items. Cool, now I have the data.
But how do I manipulate it? What's the correct way of getting a reference to the item being passed?
As I said in my comment: ReactFire is a tiny wrapper around a small subset of the regular Firebase JavaScript SDK. If you want to build an application beyond its capabilities, you can easily expand on it.
For your request I went ahead and changed the following snippet in ReactFire:
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
out.push(obj[key]);
}
}
To this:
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var item = obj[key];
item['$id'] = key;
out.push(item);
}
}
So we now pass the key() of each item as a "special" $id property of the item in the array.
With that I can expand the TodoList3 class of the original to this:
var TodoList3 = React.createClass({
handleClick: function(key) {
if (this.props.onClick) {
this.props.onClick(key);
}
},
render: function() {
var list = this;
var createItem = function(item) {
var boundClick = list.handleClick.bind(list, item['$id']);
return <li key={ item['$id'] } onClick={boundClick}>{ item.text }</li>;
};
return <ul>{ this.props.items.map(createItem) }</ul>;
}
});
So we now identify the Todo items by their $id/key(), instead of their index and use that value when the user clicks the item.
With that we can expand the JSX of TodoApp3 to pass in a handler for when the user clicks an item:
<TodoList3 items={ this.state.items } onClick={this.handleClick} />
And the app will then delete the item, by calling into the regular Firebase JavaScript SDK.
handleClick: function(key) {
var firebaseRef = this.state.ref;
firebaseRef.child(key).remove();
},
Links:
my modified ReactFire script
a fiddle showing the complete code in action

How can I pass props/context to dynamic childrens in react?

I am using react, and I am trying to pass props/context to my dynamic childrens,
by dymamic childrens I mean childrens are render using
{this.props.children}
How can I pass to this children (In my code I know it's type) context/props?
In this jsbin there is an example that it dosen't work on dynamic childrens.
http://jsbin.com/puhilabike/1/edit?html,js,output
Though #WiredPrairie's answer is correct, the React.addons.cloneWithProps is deprecated as of React v0.13RC. The updated way to do this is to use React.cloneElement. An example:
renderedChildren = React.Children.map(this.props.children, function (child) {
return React.cloneElement(child, { parentValue: self.props.parentValue });
});
There's not a a great way to do this that is clear and passing all the properties of the parent isn't a great pattern and could lead to some very difficult to follow code if not done carefully (and with excellent documentation). If you have a subset of properties though, it's straightforward:
JsFiddle
Assuming you're using React with Addons, you can clone the children of a React component and set new property values on them. Here, the code just copies a property called parentValue into each child. It needs to create a clone of each element as the child element had already been created.
var Hello = React.createClass({
render: function() {
var self = this;
var renderedChildren = React.Children.map(this.props.children,
function(child) {
// create a copy that includes addtional property values
// as needed
return React.addons.cloneWithProps(child,
{ parentValue: self.props.parentValue } );
});
return (<div>
{ renderedChildren }
</div>)
;
}
});
var SimpleChild = React.createClass({
render: function() {
return <div>Simple { this.props.id }, from parent={ this.props.parentValue }</div>
}
});
React.render((<Hello parentValue="fromParent">
<SimpleChild id="1" />
<SimpleChild id="2" />
</Hello>), document.body);
Produces:
Simple 1, from parent=fromParent
Simple 2, from parent=fromParent
Spreading props on DOM elements
https://github.com/vasanthk/react-bits/blob/master/anti-patterns/07.spreading-props-dom.md
When we spread props we run into the risk of adding unknown HTML
attributes, which is a bad practice.
const Sample = () => (<Spread flag={true} domProps={{className: "content"}}/>);
const Spread = (props) => (<div {...props.domProps}>Test</div>);

Categories

Resources