I am struggling to iterate through an array in React - javascript

I am simply trying to print a list of what is an my array into another div.
It does not seem to be working.
The code is below
import React from "react";
import "./navbar.css";
class Navbar extends React.Component{
render(){
const categories = ["Art", "Films", "Brands", "Restaraunts"];
var categoryList = categories.forEach(function(category){
return <div className='navbar-item'>{category}</div>;
})
// Real thing would equal const category = this.props.category.name;
return(
<div className='navbar'>
{ categoryList }
</div>
);
}
}
export default Navbar;
Any help would be greatly appreciated.
Thanks

Small issue. Replace forEach with map():
var categoryList = categories.map(function (category) {
return (
<div className='navbar-item'>{category}</div>
);
});
Difference between forEach and map
Let’s first take a look at the definitions on MDN:
forEach() — executes a provided function once for each array element.
map() — creates a new array with the results of calling a provided function on every element in the calling array.
What exactly does this mean?
Well, the forEach() method doesn’t actually return anything (undefined). It simply calls a provided function on each element in your array. This callback is allowed to mutate the calling array.
Meanwhile, the map() method will also call a provided function on every element in the array. The difference is that map() utilizes return values and actually returns a new Array of the same size.
Improvements
Also, quick suggestion, if I may? Use arrow functions.
var categoryList = categories.map(category => (
<div className='navbar-item'>{category}</div>
);

Use map instead of forEach. map returns a new array, forEach doesn't.

Other answers are correct here, also worth adding that when returning a list like this React will nag you to add a key (this is due to how React handles indexes with element lists). A simple way to do this is to pass the key from the map.
Borrowing from the other examples you would end up with:
var categoryList = categories.map((category, key) => (
<div key={key} className='navbar-item'>{category}</div>
);
It's worth noting that with this key, it will most likely not be very unique, especially in a complex application, and so tools eslint may insist you create one that is.

Related

Render an array of components in React/TypeScript?

I'm currently trying to render an array of JSX Components. I see two possible ways for me to do this.
One(current approach): Make the array of type object that holds the properties of the component and then construct the JSX component in line using the array contents as props
const todos = { title:string,content:string }[];
todos.map((todo:{ title:string,content:string })=>{<TitleCard title={todo.title}/>;
Two: Make the array of type JSX elements so the array stores the pure JSX Element and just plops it down with a .map inside a render function.
I find the first approach to be more clean, but it requires a bit more work in terms of constructing the JSX elements from the props in the array.
Which approach would be the correct/best way to go about this? Appreciate the help.
I'm not sure if I understand your second approach but first one sounds good. I cleaned the code up a bit and this is the way to go:
type Todo = {
title:string,
content: string
}
// this data(todos) probably will come from your props but I added some
// dummy data for demonstration purpose
const todos: Todo[] = [
{title:"title1", content:"content1"},
{title:"title2",content:"content2"}
];
const mappedTodosToRender = todos.map(({title})=>{<TitleCard key={title} title={title}/>;
return (
<>
{mappedTodosToRender}
</>
)

How to render multiple HTML parts with a plain Javascript function

This is a static website with hundreds of pages. I need to render elements like a topnav or a newsletter or a strap of content and changing those contents periodically, from JS.
This is what I tried:
const components = {
compartirEnFlex: `<h4>Newsletter</h4>`,
newsletterEs: `<h4>Compartir</h4>`,
}
const ids = ['newsletterEs', 'compartirEnFlex', 'infoArticulo', 'infoDeLaWebEnFlexIzq']
function renderComponents(objWithComp, idsArr){
return idsArr.map(function(id){
for(let component in objWithComp){
let arrOfIds = Object.keys(objWithComp);
arrOfIds.map(key => key)
if(id === key){
document.getElementById(id).insertAdjacentHTML('afterbegin', objWithComp[id])
}
}
})
}
renderComponents(components, ids);
Each id has its counterpart in the HTML structure. When I do this individually it works. However, I have to handle this in an elegant way (and there is no possibility for a JS framework like React in this project).
Thanks for the help!
When you run your code, you'll see the error Uncaught ReferenceError: key is not defined in the console.
That's because key in if(id === key) is not defined. The line arrOfIds.map(key => key) returns the same exact array as arrOfIds because Array.prototype.map "returns a new array populated with the results of calling a provided function on every element in the calling array."
Here, you don't assign that new array to a variable, so nothing happens. Even if it was, that new array would be a copy of arrOfIds because your mapping function (key) => key returns key for every key -- meaning that the output is the same as the input.
However, that's not an issue here. If I understand your question correctly, then this demo should show an example of what you're trying to accomplish. If that's what you want to achieve, then here's a solution:
First, you don't need to iterate for component in objWithComponent inside idArr -- you're already doing that in the idArr. You don't need the ids array either, because you can get the keys of the components from the components object using Object.keys().
Let's say your HTML looks something like this:
<div>
<div id="newsletterEs"></div>
<div id="compartirEnFlex"></div>
<div id="infoArticulo"></div>
<div id="infoDeLaWebEnFlexIzq"></div>
</div>
Then, using Object.keys(components) to get an array of the ids of the components that you have, you can map those to HTML tags. In fact, map is not necessary here because map returns a new array, and unless you need that array later, there's no reason to use map. Instead, you can use Object.prototype.forEach.
Here's what that would look like:
const components = {
compartirEnFlex: `<h4>Newsletter</h4>`,
newsletterEs: `<h4>Compartir</h4>`,
}
function renderComponents(objWithComp) {
Object
.keys(components)
.forEach((id) => {
const element = document.getElementById(id)
const component = objWithComp[id]
if (component && element) {
element.insertAdjacentHTML('afterbegin', component)
}
})
}
renderComponents(components)
Then, when you call renderComponents, you can pass just the components argument, and only render the components for which divs with corresponding ids exist with an if statement.

Confusion with map() in javascript

I was learning React and wanted to use map() function. Here i the code:
class App extends Component {
state = { car:[
{carTitle:'Toyota'},
{carTitle: 'Honda'},
{cartitle: 'Chevrolet'}
] };
render() {
return ( <div>
{this.state.car.map((item, index)=>
<h1>{item.carTitle}</h1>
)}
</div> );
}
}
The question is why if I use
{this.state.car.map((item, index)=>
<h1>{item[index].carTitle}</h1>
I get an error. Since I have array of objects, I think it is logical to use {item[index].carTitle}. But if I use <h1>{item.carTitle}</h1>, all works ok. Why? Did I misunderstood something?
When you use .map() you need to pass a function as a callback, like the one you are already passing: .map( (item, index) => ... )
The first argument of this callback function, item, is the current element that is been processed in the array. index is the current position.
item already returns the content of each position of the array, in this case each object that you defined in car.
So, your second example, item[index].carTitle, is incorrect because you are trying to access an array position that doesn't exist inside each object.
Also, everytime you use a .map() in ReactJS applications, you need to specify a key to the element you are returning inside this function, in this case: <h1 key={index}>{{car.carTitle}}</h1>.
Golden tip: to make your code clearer and avoid confusion you should rename car to cars, as it is a list of cars, and instead of using item you should use car. It will make your code more legible and anybody that reads your code can understand it easier than if you are using generic names. So it would look something like this:
cars: [
{carTitle:'Toyota'},
{carTitle: 'Honda'},
{cartitle: 'Chevrolet'}
]
and:
{this.state.cars.map( (car, index) => {
<h1 hey={index}>{{car.carTitle}}</h1>
})}
Further readings:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://reactjs.org/docs/lists-and-keys.html
Clean Code, Book by Robert Cecil Martin

React: Given an array, render the elements in reverse order efficiently

I currently render a list in the typical React-style. The list is passed as an array prop, and I map over it like so:
{this.props.myList.map(createListItem, this)}
So when a new element is added, it appears like the latest item was added to the end of the list.
I would like it so the latest item appears at the top. i.e. everything appears in reverse-chronological order.
The two options I've come up with so far are:
1) Reverse the list, creating a new array each time something is added, and pass this reversed list as the prop.
2) Use shift.
But they're both unappealing because of performance.
I'm not aware of Javascript supporting mapping in reverse order. I've been trying a for-loop but I haven't been able to get it to work.
What is the idiomatic way to render an array in reverse order in React?
If you choose to reverse the list using reverse(), shift() or splice(), you should make a shallow copy of the array first, and then use that function on the copy. Props in React should not be mutated.
For example:
[...this.props.myList].reverse().map(createListItem, this)
or
this.props.myList.slice(0).map(createListItem, this)
(this should really be a comment, but I don't have the points to do that yet :))
If you need to display a list in the UI in reverse order you can also use
flex-direction: row-reverse;
or
flex-direction: column-reverse;
As others have pointed out the humble reverse method does the job for most part. I currently ran into the same issue and I must say that using array.reverse() atleast in Chrome, the performance hit wasnt seen as such. In my opinion its better than using a loop to sort a list in reverse order.
array.reverse()
When using mobx as a store one can create a computed property for reversed array that will be reevaluated and memoized each time original observable array changes.
Store
import { observable, computed } from 'mobx';
class MyStore {
#observable items = [1, 2, 3];
#computed get itemsReversed() {
return this.items.slice().reverse();
}
}
export default MyStore;
Rendering
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
#inject('myStore') #observer
class List extends Component {
render() {
const { myStore } = this.props;
const { itemsReversed } = myStore;
return (
<div className="list">
{itemsReversed.map(item => (
<div className="list-item">{item}</div>
))}
</div>
);
}
}
export default List;
According to the official documentation this is a preferred way to reverse an array:
Unlike the built-in implementation of the functions sort and reverse, observableArray.sort and reverse will not change the array in-place, but only will return a sorted / reversed copy. From MobX 5 and higher this will show a warning. It is recommended to use array.slice().sort() instead.
Some how while using array.reverse() the order was changing whenever something in state changed.I went with flexDirection:'column-reverse' which worked fine and you dont need to mess with the array data aswell.
Add the new elements at the beginning of the array:
array.splice(0,0,'value to add at beginning');
Or call a for loop with an immediately invoked function:
{(() => {
for(...) {
return (<i>{whatever}</i>)
}
})()}
Keep pushing at the array, and when rendering, you can simply use the
Array.reverse()
here the documentation
Remind that it will mutate the original one
Simply first create copy of array using slice and apply reverse function when using map.
For example:
var myArr = [1,2,3,4,5]
myArr.slice(0).reverse().map((element, index) => {
console.log(element);
});

Error when attempting to render multiple ReactElements by mapping through array

I am currently looking to render multiple elements within a class by looping through an array that has been passed via props from the Owner of this component. Here is an an example:
render() {
return (
<div style={this.styles.container}>
{this.props.matchup
? this.renderMatchupWithTeams(this.props.matchup)
: this.renderDefaultMatchup()}
</div>
)
}
and then...
renderMatchupWithTeams(matchup) {
console.log('renderMatchupWithTeams', matchup);
return matchup.map(team => {
console.log(`team: ${team.name}, seed: ${team.seed}`);
return (
<Team name="UCLA"
seed={matchup.seed}/>
)
});
};
The logs were included to highlight the fact that the values are returning as expected in the logs, but the Team component is not.
Any ideas as to why the component is not rendering as expected? Please note that forEach yields the same result as map in this instance.
** code has been updated to reflect the correct answer **
Total oversight on my part. Of course, when .map() is applied to an array, a new array is projected. Thus, we must return matchups.map in order to get the new value (array) that is created (projected).

Categories

Resources