How to render multiple HTML parts with a plain Javascript function - javascript

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.

Related

I am struggling to iterate through an array in React

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.

Rerendering mapped array based on order

So I have an array of objects that I need to display in a specific order and be able to organize. The functionality is there and it works, but I can't get it to render appropriately.
Basically I have:
const [objects, setObjects] = useState([]);
return (
<>
{objects.map(object =>
<Component
object={object}
/>
)}
</>
);
Now I obviously have this array filled with data and it renders fine, but when I try to reorganize the array, nothing seems to change on the UI.
I'm reorganizing by way of splice. I identify the object that I'm moving, splicing it out and splicing it back in at a different index (not actually sure if I need this temporary variable):
function reorganize(sourceIndex, targetIndex){
const object = objects[sourceIndex];
const temp = objects;
temp.splice(sourceIndex, 1);
temp.splice(targetIndex, 0, object);
setObjects(temp);
}
When I use console.log() on objects, I can see that the order has changed. But as I said, nothing changes on the UI. Any thoughts on how to make this work?
in the reorganize function change this
const temp = [...objects];
Don't forget that it is an array.
You are mutating the state - temp is not a copy of objects, but is still directly referencing it. In React you should not directly mutate the state - correct code would be temp = [...objects]. Also be aware that this will only create a shallow copy - if you are handling deeply nested arrays / objects, use an external library like Lodash's cloneDeep.

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

Passing associative array as props not working

I have a React application which handles rooms and their statistics.
Previously, I had the code set up to pass as props to the next component:
the raw statistics (not a concern for the question)
an array of all the rooms set up as follows
I figured it would be simpler for me, though, to have the list of all rooms as an associative array where the keys of each element is the same as the ID it contains. To do that, I utilized a code similar to this in a for loop:
roomsList[rooms[v].ID] = rooms[v];
So that the result would be:
[a001: {...}, a002: {...}, ...]
I then proceeded to pass this style of array, and not the standard one with a numeric index, as a prop to the next component as such:
<StatsBreakdown stats={computedStats.current} roomsList={roomsList} />
BUT
Now, the next component sees that prop as an empty array.
Even more weirdly, if I initialize that roomsList array with a random value [0] and then do the same process, I end up with:
I cannot cycle through the array with .map, and, according to JS, the length is actually 0, it's not only Google Chrome.
Is there something I'm missing about the way JSX, JS or React work?
Your original roomsList was an array of objects, whose indices were 0,1,2 etc. roomsList[rooms[v].ID] = rooms[v]; implies you are inserting elements not using a number but an alphanumeric string. Hence your resulting array is no longer an array but an object.
So we can cycle over the object using Object.keys().
const renderRoomDets = Object.keys(roomsList).map(room => {
roomOwner = roomsList[room].owner_id;
return (
<div>
<p>{`Room Owner ${roomOwner}`}</p>
</div>
);
});
But I believe your original form is ideal, because you are reaping no special benefits from this notation.
A better alternative maybe using .find() or .findIndex() if you want iterate over an array based on a specific property.
const matchedRoom = roomsList.find(room => room.ID === 'Srf4323')
Iterate the new array using its keys not indexes.
Or even better store your data in an actual object instead of an array since you're using strings for ids.
First define your object like so:
let data = {};
Then start adding records to it. I'd suggest deleting the ID attribute of the object since you're storing it in the key of your record, it's redundant, and it won't go anywhere unless u delete the entry.
data[ID] = row;
To delete the ID attribute (optional):
row.ID = null;
delete row.ID;
Then iterate through it using
for(let key in data){}

Loop through nested dataLayer array to return pipe delimited strings

We have a nested dataLayer variable on our booking platform. Users can make one or multiple variables are we want to pull out a string containing each of the product types contained within the array. I am hitting a error when debugging this however.
The location of the variable I would like to collect is:
dataLayer.booking.products[i].travelType
try{
var productList = {};
for(i=0;i<dataLayer.booking.products.length;i++){
productList[dataLayer.booking.products[i].travelType];
}
return productList.join('|');
}
catch(err){}
I am naive with JS so I apologies for a basic question.
M
Your code shows that you're setting a new property of the object productList, but you're not defining a value, e.g. {foo: } instead of {foo: "bar"}. It looks like what you want is an array that you can add strings to. For example:
var productList = dataLayer.booking.products.map(function(product) {
return product.travelType;
});
return productList.join('|');
Note that this is using the Array's map method as opposed to your for loop. You could also define productList as an array in a previous line, and then use the forEach method on the products Array to loop through every item, but I think this is cleaner and still legible. You can reduce the code further with ES6 syntax, but for your question it's probably better to show code that is more clearly defined.

Categories

Resources