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).
Related
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.
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.
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
I recently needed to use the Float32Array typed-array, which will be used to render a list of elements (regardless in what type of format - e.g. an <ul> with <li> as each element of my array). For some reason, when I try to render the values, I get NaN even though the isNaN() call returns false moreover the typeof of each element is number and the console prints just fine the values. For example:
class App extends Component {
constructor(props) {
super(props);
this.state = { channels: new Float32Array([1, 2, 3, 4]) };
}
render() {
return (
<ul>
{this.state.channels.map((val, index) => (
<li key={index}>{val}</li>
))}
</ul>
);
}
}
export default App;
This one prints a series of NaNNaNNaNNaN (not even distributing them in proper <li> elements. I expect to have the values printed as they are already stored in the array.
Is there any reason for which the rendering doesn't work for types arrays (as expected) but works just fine in console logging? (or am I doing actually something wrong?)
Thanks!
The reason it's doing that is because Float32Array#map() expects the callback to return a number, and the method itself returns a Float32Array. What you'd want to do is convert it to an Array first and then map it:
Array.from(this.state.channels).map((val, i) => ...)
Using React, how do I render a <div> with multiple nested <div>'s inside them? See code below.
var SubTrait = React.createClass({
render: function() {
var subTraits = this.props.data;
return (
<div className = "subTraitContainer">
<div>
{ subTraits.forEach(function(subTrait) {
return (
<div>
<h4>{ subTrait.name }</h4>,
Sub-Sub-Traits
<SubSubTrait data={ subTrait.children } />
</div>
);
})}
</div>
</div>
);
}
});
The end result I get with this code is:
-edit-
Would the proper way to do this be to create my nodes first using .forEach, then passing the created nodes to the render function's return? (I'm looking at the React Tutorial at React Docs. Would my original solution be possible, or is the only way to pre-create the nodes before returning them?
You need to use map instead of forEach. forEach returns nothing, so the result is:
<div class="subTraitContainer">
<div></div>
<div>
map returns an array. It maps each element in the given array to something new. For example, you could map an array of Users to their usernames. If you use map instead of forEach, you will get an array of <div>s, which should do what you expected.