I'm having trouble writing a for loop in react.js - javascript

I'm new to programming and I'm learning Facebook's react.js. I found a website that has a tutorial which builds a "shopping cart" using react. I tried to modify it to add more items using a for loop but it keeps giving me "unexpected token }" errors followed by this error:
"Invariant Violation: FluxProduct.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object."
I realize there is a similar question that was answered but it didn't help me.
There is quite a bit of code in this project but the particular spot I'm having trouble with looks like this:
render: function() {
var rows = [];
var ats = (this.props.selected.sku in this.props.cartitems) ?
this.props.selected.inventory - this.props.cartitems[this.props.selected.sku].quantity :
this.props.selected.inventory;
for (var i=0; i<2; i++){<div className="flux-products">
<img src={'img/' + this.props.product.image}/>
<div className="flux-products-detail">
<h1 className="name">{this.props.product.name}</h1>
<p className="description">{this.props.product.description}</p>
<p className="price">Price: ${this.props.selected.price}</p>
<select onChange={this.selectVariant}>
{this.props.product.variants.map(function(variant, index){
return (
<option key={index} value={index}>{variant.type}</option>
)
})}
</select>
<button type="button" onClick={this.addToCart} disabled={ats > 0 ? '' : 'disabled'}>
{ats > 0 ? 'Add To Cart' : 'Sold Out'}
</button>
</div>
</div>}
return ("flux-products-detail"
);
},
});
If you want/need the original code and the rest of the project I'd be more than happy to provide it.

It looks like you are trying to compose three components in the for loop. However the result of the for loop is not stored to a variable, and not rendered properly in the return function.
// Use a variable to store the result of the loop
return (
<div>
{myComponent}
</div>
)

In the case of using a loop to generate content for a React element, I usually formulate it something like this:
render: function() {
var items = [];
for(var i = 0; i < this.state.listOfItems.length; i++) {
items.push( <ItemComponent value={this.state.listOfItems[i].value} />
}
return ( <div> {items} </div>)
};
So what you need to do is return from render some JSX for react to Render. as #FelixKling said, JSX isn't magic, you're essentially just returning a bunch of React.createElement functions.

Related

Can we pass fake keys to children in React.js?

I have created a component and it's running well in local server. But I am getting below warning
Warning: Each child in a list should have a unique "key" prop.
Getting this warning means we need to fix the key index props? as given here.
below is some snippets of my component code..
render() {
return (
<div>
<Container>
<Row>
<Col className="col-12">
{this.state.client.map((val,index)=>{
if(index == this.state.colaborators.length -1)
return <a href={"/users/"+val}>{val}</a>
return <a href={"/users/"+val}>{val} ,</a>
})}
</Col>
</Row>
</Container>
</div>
</div>
</div >
);
}
}
export default App;
I checked some solution from here
As I told my code is working well. Can we use some fake key props? for example
key={fake index}
And we are using will this affect in my working code?
If this.state.client ever changes, don't just use the index (which is sadly common); see this article for why and its demo of what can go wrong. You can only do that with a list that never changes, or only grows/shrinks (and not at the same time), not with one where the order changes (you insert at the beginning, or sort, or...) More in the docs.
I'm guessing val will be unique in the list, so use that as the key:
{this.state.client.map((val, index) => {
const href = "/users/" + val;
const display = index == this.state.colaborators.length - 1 ? val : `${val} ,`;
return <a key={val} href={href} >{display}</a>;
})}
If your lists order is not going to change, simply use:
return <a key={index} href={"/users/"+val}>{val}</a>
return <a key={index} href={"/users/"+val}>{val} ,</a>
It will not affect your code and it will remove the warning.

Proper way of object copy from one element to another in Vue.js

I am new to Vue.js (I mostly use PHP) and I am trying to creating simple view where user can add an object from one component and place it's copy into another component.
Main template
<template>
<div class="left">
<TaskList :tasks="tasks" v-on:pinned-add-task="pinnedAddTask" />
</div>
<div class="right">
<PinnedList :pinned="pinned" />
</div>
</template>
TaskList
<template>
<div class="task-list">
<div v-for="task in tasks" :key="task.id">
<TaskItem :task="task" v-on:pinned-add-task="$emit('pinned-add-task', task)" />
</div>
</div>
</template>
TaskItem
<template>
<div>
<p>{{task.name}}</p>
<button v-on:click="$emit('pinned-add-task', task)">+</button>
</div>
</template>
And as far as I am aware object "task" is passed by reference and when I try to create an empty object or an array and insert "task" into that newly created object/array when I change original "task" it is also being changed inside that new object and I don't want that.
I am getting my data (tasks) from API that I have created and I am using pagination system so I want to be able to switch pages without losing it from the pinned page.
I created code which looks like this but I don't like it and I don't think that's a good way to do this:
pinnedAddTask(item) {
let pQuantity = 1; // I use this value because I want to be able to pin one task multipletimes
let left = this.pinned;
let right = [];
for (let task of this.pinned) {
if(item.id == task.id) {
pQuantity = task.quantity + 1;
left = this.pinned.filter(eItem => eItem.id < item.id);
right = this.pinned.filter(eItem => eItem.id > item.id);
}
}
const clone = {...item, quantity: pQuantity};
this.pinned = [...left, clone, ...right];
}
Can anyone confirm or reject this?
Yes this one is fine if thats just a shallow copy [ level-one object].
But if you are having a nested object then you might have to use recursive methodology or use any external libary like lodash

How to inject a dinamically created element into an existing div in React JSX?

I have a list of objects photos, from a json data file, that I would like to organize into 3 different <div> columns, but I dont know how to achieve that, here is my broken non-optimized code:
<div className="container">
<div ref={leftColRef} className="left-col" />
<div ref={centreColRef} className="centre-col" />
<div ref={rightColRef} className="right-col" />
{Object.keys(photos).forEach((n, i) => {
const id = photos[n].id;
const thumb = photos[n].thumbnailUrl;
const title = photos[n].title;
const element = (
<Thumbnail id={id} title={title} thumb={thumb} />
);
if (i % 3 === 0) {
leftColRef.current.append(element);
} else if (i % 3 === 1) {
centreColRef.current.append(element);
} else {
rightColRef.current.append(element);
}
// this line works, it idsplays the data but is commented as the data needs to go inside its respective columns
// return <Thumbnail key={id} title={title} thumb={thumb} />;
})}
</div>
The idea is to insert some elements into the left-column when i%3 = 0 and others in the centre-column when i%3 = 1 and so on ...
And a link to my codesandbox
Any help/advise will be much appreciated.
Easiest is probably to prepare the data outside the render function and to render the column one by one.
You should not manipulate the DOM like it's done in jQuery using JSX
Example:
const Component = (props) => {
const filterPhotos = (column) => {
return props.photos.filter((photo,index)=> index%3==column);
}
return <>
<MyColumn photos={filterPhotos(0)}/>
<MyColumn photos={filterPhotos(1)}/>
<MyColumn photos={filterPhotos(2)}/>
</>;
}
First, using ref on div to inject stuff on it is wrong. It's the opposite of how react works.
Like charlies said, I would split the photos in 3 different arrays before the render. Then, you'll be able to do something like this :
<div ref={leftColRef} className="left-col" />
{ photosLeft.map(photo => <Thumbnail key={photo.id} {...photo} />)
</div>
when preparing your data, try to use the same object properties and component props name so you can spread it easily ( {...photo} ).
Note: Also, when rendering an array in react, each child must have a unique key props. It will help react to render on that part of dom if your data change.

Passing a method down two levels

I'm trying to build a simple little game for fun, but I'm hitting a stump when it comes to passing this method down to its grandchildren. I've found similar topics, but none that answered my question. For this question, I've managed to narrow the code down to what the problem is perceived to be.
The relevant component structure is App.js > Runeboard.js > Rune.js
The Goal: To have Rune.js have an onClick function on each rune that is dynamically produced by whatever is in this.state.usersRunes.
I don't believe there is anything wrong with the function itself, or passing it down as props, because console logging the runes values all succeed when in Runeboard.js, and even in Rune.
This works, but is not dynamic to what is in the this.state.usersRunes array:
return (
<div>
Runeboard
<span onClick={() => this.props.activateRune(this.props.usersRunes[0])}> {this.props.usersRunes[0]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[1])}> {this.props.usersRunes[1]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[2])}> {this.props.usersRunes[2]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[3])}> {this.props.usersRunes[3]} </span>
<br />
<br />
</div>
);
The problem with that, is I'd like for every item in this.state.usersRunes (an array of integers), a Rune component that has its own onClick that successfuly executes activateRune with its parameter of the value of the rune. (The value of the rune being this.state.usersRunes So this.state.usersRunes = [2,3,5,9] the values would be 2, 3, 5, and 9.
So even though the above works, this does not and I do not understand why:
App.js
The activateRune function:
activateRune(rune) {
if (this.state.inBet || this.state.mustBet) {
this.call(rune)
} else if (!this.state.inBet) {
this.setMessage("You cannot place a rune first!")
}
}
App.js render:
<Runeboard
activateRune={this.activateRune}
usersRunes={this.state.usersRunes}
/>
Runeboard.js
render:
let rune = this.props.usersRunes.map((rune, i) => {
console.log(rune) // this works and successfully prints the array's integers
return(
<div>
<Rune activateRune={this.props.activateRune}
runeValue={rune} key={i} />
</div>
)
})
return(
<div>
{rune}
</div>
)
Rune.js
render:
return (
<div onClick={() => this.props.activateRune(this.props.runeValue)}>
{this.props.runeValue} // this works and successfully displays the value
</div>
);
How can I resolve this?
I think it might be an issue with your syntax.
<div onClick={() => {this.props.activateRune(rune)}}>
If you use curly braces, {}, you need a return value:
<div onClick={() => {return this.props.activateRune(rune)}}>
You can also avoid the curly braces and have it be:
<div onClick={() => this.props.activateRune(rune)}>
Shoutout to #xiiishaneiiix and #haopeng for the fast answers! Their comments/answer helped me try out some new things, which made me test some things and narrow what the cause was.
Turns out it was within Runeboard, in a conflict with the parameter and variable name being the same.
let rune = this.props.usersRunes.map((rune, i) => {
return(
<div>
<Rune activateRune={this.props.activateRune} runeValue={rune} key={i} />
</div>
)
})
return(
<div>
{rune}
</div>
)
Having the same name, the property I was trying to pass was actually grabbing the variable with the same name, and not the parameter from map.
The solution was in renaming the variable and parameter, clearing telling the code that we want the parameter from the map and not the variable:
let runes = this.props.usersRunes.map((runeValue, i) => {
return(
<div>
<Rune activateRune={this.props.activateRune} runeValue={runeValue} key={i} />
</div>
)
})
return(
<div>
{runes}
</div>
)

React Child Component Loop not redenring

Well I have one of the views from my single page application that is a Quiz, But when a click to generate the Components through a loop based on information on my array state he doesn't render it. I'm using react-router in the index.js maybe this information can help.
Gabarito.js
return(
<div>
<h1 className="display-1">Resposta-#{this.props.chave}-
{this.props.alternativa}</h1>
</div>);
Quiz
state = {
respostas:[10],
gabarito:['Verdadeiro','Falso','Falso','Verdadeiro','Verdadeiro','Falso','Verdadeiro','Falso','Verdadeiro','Verdadeiro'],
correcao:[],
novogabarito: this.eachcomponent
}
alterarevento = (evento,index) =>{
let array = this.state.respostas;
array[index] = evento.target.value;
this.setState({respostas:array});
console.log(this.state.respostas[index])
}
gerargabarito = () =>{
for(let n=0;n<10;n++){
if(this.state.respostas[n]===this.state.gabarito[n]){
this.state.correcao.push('Certa');
}
else{
this.state.correcao.push('Errada');
}
}
console.log(this.state.correcao);
}
eachcomponent = () =>{
return(this.state.correcao.map(resposta=><Gabarito chave={this.state.correcao.indexOf(resposta)} alternativa={resposta}/>));
}
Render of function
<div className="row justify-content-center">
<span id="teste">{this.state.novogabarito}</span>
</div>
</div>
);
}
}
Perhaps I am overlooking something...but it does not look like you are ever invoking your alterarevento and gerargabarito functions. So when you call your eachcomponent your correcao array in your state is still empty so you are not mapping anything.
Before your return statement in your eachcomponent function, try logging correcao to the console to see if it is empty.
A word of caution: you should never manipulate your state directly. So, your this.state.correcao.push('Certa');
line should be:
this.setState({ correcao: [...this.state.correcao, 'Certa'] });

Categories