List elements not rendering in React [duplicate] - javascript

This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 26 days ago.
This is my Sidebar component.
const Sidebar = React.createClass({
render(){
let props = this.props;
return(
<div className= 'row'>
<h2>All Rooms</h2>
<ul>
{props.rooms.map((room, i) => {
<li key={i}> {room.name} </li>
})}
</ul>
{props.addingRoom && <input ref='add' />}
</div>
);
}
})
This is where I render it populating one room.
ReactDOM.render(<App>
<Sidebar rooms={[ { name: 'First Room'} ]} addingRoom={true} />
</App>, document.getElementById('root'));
The contents inside the <ul></ul> tag don't render at all. Any idea what I could be missing.

You aren't returning anything from the map function, so it renders nothing inside the unordered list. In your arrow function, you do:
(room, i) => {
//statements
}
This doesn't return anything. Array.prototype.map requires a callback that returns a value to do anything. map transform elements to the array, mapping (executing) the callback on each element. The return value is what the value is in the corresponding position in the returned and mapped array.
You must return the list element in the map function like so:
<ul>
{props.rooms.map((room, i) => {
return <li key={i}> {room.name} </li>;
})}
</ul>
Now, the mapped array of list elements is rendered because the list element is returned.
To make this shorter, you may write it in the form (params) => value which is equivalent to the above, where value is the returned value, like so:
{props.room.map((room, i) => <li key={i}> {room.name} </li>)}

Related

React.js: How to filter JSX element array on custom attribute?

I am starting with a simple array of JSX elements:
const jsxArray = dataItems.map(item => (
<div>
<Header>{item.title}</Header>
<Paragraph>{item.body}</Paragraph>
<Paragraph customAttribute={item.isActive} >{item.tags}</Paragraph>
</div>
))
Inside render, or rather return since I use functional components for everything now, I'd like to filter for JSX elements where the isActive attribute was tagged true.
return (
{jsxArray
.filter(jsxElement => // want to filter in JSX elements
// that are true for customAttribute keyed to `item.isActive`)
}
)
Is there any way to do it?
If there is not precisely a good way I am open to workarounds.
It is possible for me to simply filter the array at an earlier step. It would result in some extra code duplication though, since I would still need the array of unfiltered JSX elements elsewhere.
You don't filter the list after you render it. At that point it's just a tree of nodes that doesn't have much meaning anymore.
Instead you filter the items first, and then render only the items that pass your criteria.
const jsxArray = dataItems.filter(item => item.isActive).map(item => (
<div>
<h3>{item.title}</p>
<p>{item.body}</p>
<p customAttribute={item.isActive} >{item.tags}</p>
</div>
))
It is possible for me to simply filter the array at an earlier step. It would result in some extra code duplication though, since I would still need the array of unfiltered JSX elements elsewhere.
Not necessarily. When dealing with filtering like this myself I create two variables, one for the raw unfiltered list and one for the filtered items. Then whatever you're rendering can choose one or the other depending on its needs.
const [items, setItems] = useState([])
const filteredItems = items.filter(item => item.isActive)
return <>
<p>Total Items: ${items.length}</p>
<ItemList items={filteredItems} />
</>
Instead of accessing the jsx element properties (which I think it's either not possible or very difficult) I suggest you to act in this way:
Save the renderer function for items in an arrow function
const itemRenderer = item => (
<div>
<Header>{item.title}</Header>
<Paragraph>{item.body}</Paragraph>
<Paragraph customAttribute={item.isActive} >{item.tags}</Paragraph>
</div>
)
Save the filter function in an arrow function
const activeItems = item => item.isActive
Use them to filter and map
const jsxArray = dataItems.filter(activeItems).map(itemRenderer)
Use them to map only
const jsxArray = dataItems.filter(activeItems).map(itemRenderer)
Hope this helps!
Usually you would filter the plain data first and then render only the markup for the filtered elements as described in #Alex Wayne answer.
If you worry about duplication of the markup, that can be solved by extracting a component from it:
const Item = ({title, body, isActive, tags}) => (
<div>
<Header>{title}</Header>
<Paragraph>{body}</Paragraph>
<Paragraph customAttribute={isActive}>{tags}</Paragraph>
</div>
);
For rendering the filtered list you can then do:
{items.filter(item => item.isActive).map(item => <Item {...item} />)}
and for the unfiltered list:
{items.map(item => <Item {...item} />)}

React incrementing variable within .map() function

I am mapping through an array, and I want my variable i to be used as a unique key for my Components, however I do not know how (or where) to increment it correctly, if I add a {i++} within the <Component> tags then it will display the value of i on screen, and if I instead add {this.function(i)} and place the i++ inside the function, it will call the function but the variable i will reinitiate to the value of 0 everytime, so the key value will not be unique. I need the value of i to be the key for the component and it has to be incremented by 1 everytime, does anyone know how I can achieve this? Also, as you can see in the code, when the component is clicked it will make a function call which will send the value of i of the clicked component as a parameter to the called function.
Code:
function(i) {
console.log(i)
}
render() {
var i = 0;
var {array} = this.state;
return (
<div className="App">
{array.map(item => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
))}
</div>
);
}
The map function gets a second parameter which is the index of the element:
{array.map((item, i) => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
)}
Be aware that if you intend to sort this array or change its contents at runtime, then using array index as a key can cause some mistakes, as sometimes an old component will be mistake for a new one. If it's just a static array though, then using index as a key shouldn't be a problem.
.map already offer the increment, just add a second variable to the callback
render() {
var {array} = this.state;
return (
<div className="App">
{array.map((item,i) => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
))}
</div>
);
}
You could try array.map((x, Key) => console.log(key)); ..
In place of console.log you could add your code, it should work fine as per your requirement.

Using Array.map in React

I'm confused how this code work
const sidebar = (
<ul>
{
props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)
}
</ul>
);
I know that a map returns an array, but how can it return all these tags
<li key={post.id}>
{post.title}
</li>
and render it?
I have tried changing it to forEach but it's not working. Nothing displays on the UI:
const sidebar = (
<ul>
{
props.posts.forEach((post) =>{
return <li key={post.id}>
{post.title}
</li>
})
}
</ul>
);
The map() method creates a new array with the results of calling a
provided function on every element in the calling array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The forEach() method executes a provided function once for each
array element.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
This means if you try to return something in the method executed in forEach you are not creating an array of tags.
JSX is expecting an array of tags to be rendered but in forEach you are not creating an array you are just iterating over the array so JSX receives nothing at the end

Using a map within a map in jsx

{normalizedData.map(obj =>
<div key={obj.display_date_numberic}>
<div>{obj.display_date_numberic}</div>
</div>
{!isEmpty(obj.applicants) && obj.map(obj2 =>
<div className="events">{obj2.person.name}</div>
)}
)}
I'm getting an error on the following line:
{!isEmpty(obj.applicants) && obj.map(obj2 =>
Why can't I use the map function inside another map? normalizedData has an array of objects and each obj has another array of objects.
You can do a map within a map as follows:
e.g. given data of
outerArray: [
{ id: '1st', innerArray: [ 'this', 'that' ]},
{ id: '2nd', innerArray: [ 'some', 'what' ]},
]
you can use JSX:
<ul>
{outerArray.map(outerElement => {
return outerElement.innerArray.map(innerElement => (
<li key={outerElement.id}>
{innerElement} - {outerElement.id}
</li>
))
})}
</ul>
or slightly more succinctly:
<ul>
{outerArray.map(outerElement => (
outerElement.innerArray.map(innerElement => (
<li key={outerElement.id}>
{innerElement} - {outerElement.id}
</li>
))
))}
</ul>
which renders:
<ul>
<li>this - 1st</li>
<li>that - 1st</li>
<li>some - 2nd</li>
<li>what - 2nd</li>
</ul>
note the use of key= inside the map to ensure React has a unique reference for the element from each loop iteration. If you don't it will warn you!
Reason is, you are trying to render more than one element, and we can't do that, we can return only one element. So wrap all the logic and elements inside one div like this:
{normalizedData.map(obj =>
<div key={obj.display_date_numberic}>
<div>{obj.display_date_numberic}</div>
{Array.isArray(obj.applicants) && obj.applicants.map(obj2 =>
<div className="events">{obj2.person.name}</div>
)}
</div>
)}
Assuming obj.applicants is an array, use Array.isArray to check whether any value is a proper array or not
Note: We can use map only on array not on any object, so if obj is an object and use Object.keys(obj) to get an array of all the keys then use map on that.
The evident error that you hace in your code is that , you should be mapping on obj.applicants in the inner map and not obj and return a single element from the outer map
Also if obj.applicants is an array, no need to use isEmpty
{normalizedData.map(obj =>
<div>
<div key={obj.display_date_numberic}>
<div>{obj.display_date_numberic}</div>
</div>
{ obj.applicants.map(obj2 =>
<div className="events">{obj2.person.name}</div>
)}
</div>
)}
Just advice, you can use for instead of map

Getting key prop warning in React, even though key is set

Problem
I'm getting this warning:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of EventsTable. See fb.me/react-warning-keys for more information.
react-runtime-dev.js?8fefd85d334323f8baa58410bac59b2a7f426ea7:21998 Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of Event. See fb.me/react-warning-keys for more information.
Source
This is EventsTable:
EventsTable = React.createClass({
displayName: 'EventsTable',
render() {
console.dir(this.props.list);
return (
<table className="events-table">
<thead>
<tr>
{_.keys(this.props.list[0]).map(function (key) {
if (key !== 'attributes') {
return <th>{key}</th>;
}
})}
</tr>
</thead>
<tbody>
{this.props.list.map(function (row) {
return (
<Event key={row.WhatId} data={row} />
);
})}
</tbody>
</table>
)
}
});
This is Event:
Event = React.createClass({
displayName: 'Event',
render() {
return (
<tr>
{_.keys(this.props.data).map((x) => {
if (x !== 'attributes')
return <td>{this.props.data[x]}</td>;
})}
</tr>
)
}
});
Question
Clearly I've got the key prop on the <Event /> component. And I'm following the convention that you're supposed to include key on the component, not on the HTML itself (in other words, HTML tags within the Event component). Per the official React docs:
The key should always be supplied directly to the components in the array, not to the container HTML child of each component in the array:
I'm severely confused. Why am I getting warnings?
Have you tried adding a key to the <th> tag?
<tr>
{_.keys(this.props.list[0]).map(function (key) {
if (key !== 'attributes') {
return <th key={key}>{key}</th>;
}
})}
</tr>
I ended up solving it when I realized because I had a <React.Fragment> which also needs a unique key.
tl;dr
Every time you render a list (use map), add a unique key attribute to the list elements (the topmost or "root" element returned from map's callback):
render() {
return (
<div>
{this.props.data.map( element => {
// Place the key on to the returned "root" element.
// Also, in real life this should be a separate component...
return <div key={element.id}>
<span>Id (Name): </span>
<span>{element.id} </span>
<span>({element.name})</span>
</div>;
})}
</div>
)
}
Explanation
Understanding keys in React
The official Lists and Keys documentation shows how you should work with lists and the linked reconciliations doc tells the whys.
Basically when React rerenders a component it runs a diff algorithm that finds out what changed between the new and the previous version of the list. Comparison is not always trivial, but if there is a unique key in each element, it can be clearly identified what has changed. See the example in the doc:
<!-- previous -->
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<!-- new -->
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
It is clear that a new element with the key 2014 was added, since we have all the other keys and those weren't changed. Without the keys this would be obscure.
Selecting a proper key
From now it is easy to see:
Why it is important that the key should be unique but only between the siblings in the list, because the comparison happens only within the given list's previous and new elements.
The key should remain the same for the same element between the previous and the new version, otherwise we would compare different elements and wouldn't be able to track change. That is why it is advised to use the id of the resource or (if it doesn't have one) some other data that is unique to the element, and why you shouldn't use things like Math.random().
Placing key attribute on components
The convention that you should place the key attribute to a component is more of a good practice, because when you iterate a list and want to render an element, that clearly indicates that you should organize that code to a separate component.
Setting the key attribute in the loop
The statement you quoted from the docs:
The key should always be supplied directly to the components in the array, not to the container HTML child of each component in the array:
Means that if you render components in a loop, then you should set the key attribute of the component in the loop, like you did it in your EventsTable component:
{this.props.list.map(function (row) {
return (
<Event key={row.WhatId} data={row} />
);
})}
The wrong way is to pass it down to the component where it would set the key on itself:
Event = React.createClass({
displayName: 'Event',
render() {
// Don't do this!
return (
<tr key={this.props.data.WhatId}>
{_.keys(this.props.data).map((x) => {
There is another good example for this in this article.
Check if variable that you pass to key is defined, because if it's undefined then error will be same, but it looks like code should work.
I had the problems too, and fixed it after follwing link.
like:
{_data.map(function(object, i){
return <div className={"row"} key={i}>
{[ object.name ,
<b className="fosfo" key={i}> {object.city} </b> , // remove the key
object.age
]}
</div>;
})}
The easiest fix for this is to create a separate component for the items you're mapping and add the key to that component.
Create a new component above your existing component (or link to it your call).
const TableDataComponent = ({ k }) => {
return (
<th>{k}</th>
)
}
Then in your code add that component with your key:
<tr>
{arr.map((k) => {
return <TableDataComponent key={k._id} k={k} />
})}
</tr>

Categories

Resources