push element from one array to another on button click in React? - javascript

I am trying to use the method .contact() to push on element in my old_array to my new_array.
I have one button on each element in the array like this:
´´´
<li key={i}>
{{character.name} + "is" + {character.age} + "years old"}
<button onClick={this.addToNewArray}>Fav</button>
</li>
´´´
so as you can see each element got a seperate id. Now i want to click the button to push that element to a new array . (i get data from API that i .map() into my old_array) My function looks like this:
´´´
constructor(props){
super(props);
this.state = {
old_arary: [],
new_array: []
}
}
addToNewArray = () => {
let new_array = this.state.new_array.contact(this.state.old_array);
this.setState({ new_array: new_array})
}
´´´
This is where i want my output:
´´´
<li>
{this.state.new_array}
</li>
´´´

First :
in your question , you are using contact() everywhere, and I think there is no such function for array in JS :) , that should be concat()
Second :
You can use ES6 for lower code, something like this:
let new_array = [...this.state.new_array , ...this.state.old_array];
this.setState({ new_array });
Third :
Change this
<li>
{this.state.new_array}
</li>
To :
{
this.state.new_array.map((obj,index) => (
<li key={index}>
{obj.name}
</li>
))
}

Related

For Loops and React JS

So I have this function here:
const printCardList = (arr) => {
const uo_list = document.getElementById("verify_list");
arr.forEach((card) => {
let list_item = document.createElement("LI");
let str = card.name + " " + card.mana_cost + " " + card.set_name;
list_item.appendChild(document.createTextNode(str));
uo_list.appendChild(list_item);
});
};
and its suppose to insert list items into and unorder list from an array of card objects.
return(
<div className="list-confirm">
<h3> Please confirm card list </h3>
<ul id="verify_list"></ul>
<br />
<button onClick={getCardList}>Confirm</button>
</div>
);
If I do a console.log on arr I can verify that it is an array of cards, but if I console.log card from inside the for each it does not even trigger. It's like the for each does not run. Does anyone have an idea why this is happening?
I'm not sure what you are trying to do, the first part of your code is plain javascript that manipulates the DOM, while the second part is react js object.
You normally don't want to mix these two, either you code your javascript as part of the html, like the first part, or - if you want to create an array of cards in react you can do something like:
let cardList = arr.map(card => {
listItem = <li>{card.name + " " + card.mana_cost + " " + card.set_name }</li>
return listItem;
})
return(
<div className="list-confirm">
<h3> Please confirm card list </h3>
<ul id="verify_list">{cardList}</ul>
<br />
<button onClick={getCardList}>Confirm</button>
</div>
);
what I did is assigned the list itself to a variable named 'cardList', JSX object are just javascript objects, so you can assign them to a variable or return then from a function.
to place the card list inside the page (or component), you can just use the {} notation, which will embed the cardList object as part of the returned JSX object.
Thanks for all the advice. In hindsight, I should have stuck to what I was learning and not try to freestyle. React is about using states. So rather than having a function that will generate HTML from an array of data and I had to do use "the state". Then code the render to loop through the list of cards when the button is pressed.
const [state, setState] = useState([]);
const card_list= ()=> {...}
const changeState = ()=> {setState(card_list)}
return(
<div className="list-confirm">
<h3> Please confirm card list </h3>
<ul>
{state.map((card) => (
<li>{card.name}</li>
))}
</ul>
<br />
<button onClick={changeSate}>Confirm</button>
</div>
);
You should change the onClick. More precisely call the method after getting items from getCardList() method.
This is an example:
const printCardList = (arr) => {
const uo_list = document.getElementById("verify_list");
arr.forEach((card) => {
let list_item = document.createElement("li");
let str = card.name + " " + card.mana_cost + " " + card.set_name;
list_item.appendChild(document.createTextNode(str));
uo_list.appendChild(list_item);
});
};
// this is a test method. Not the real one
const getCardList = () => {
return [ { name: "Card", mana_cost: 0, set_name: "Set: Card" } ];
};
<div className="list-confirm">
<h3> Please confirm card list </h3>
<ul id="verify_list"></ul>
<br />
<button onClick="printCardList(getCardList())">Confirm</button>
</div>

Get attributes for all (unknown) <li> elements with Javascript

I need to concatenate all the title value starting from second li elements with Javascript.
The problem is that I want to use it in different pages, so I can't know the exact number of li elements.
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
I use a variable for each element but if one or more element is missing the var is not valid and the concat function doesn't work.
var a = document.querySelector(".breadcrumb li:nth-child(2) > a").getAttribute("title");
var b = document.querySelector(".breadcrumb li:nth-child(3) > a").getAttribute("title");
var c = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var d = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var str = a.concat(b,c,d);
console.log(str)
Is there a way to do that?
Use querySelectorAll() and map():
const res = [...document.querySelectorAll("#breadcrumb li:not(:first-of-type)")].map(el => el.getAttribute("title")).join(" ")
console.log(res)
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
Using a little jquery i achieved this and it should solve your issues.
let list = [];
$('#breadcrumb li').each(function(i){
if(i !== 0) { list.push($(this).attr('title')); }
});
list.toString() //One,Two,Three,Four
The method you tried to use wont scale on a large list
Two minor remarks:
If you want to access the id=breadcrumb, you have to use #breadcrumb instead of .breadcrumb
There is no a tag in your HTML-code, therefore your querySelector won't give you any result
However, let's discuss a solution:
let listElements = document.querySelectorAll("#breadcrumbs li"); // get all list elements
listElements = Array.from(listElements); // convert the NodeList to an Array
listElements = listElements.filter((value, index) => index >= 1); // remove the first element
let titleAttributes = listElements.map(listElement => listElement.getAttribute("title")); // get the title attributes for every list elements. The result is an array of strings containing the title
console.log(titleAttributes.join(", ")); // concatenate the titles by comma
You can write the above statements in a single line:
Array.from(document.querySelectorAll("#breadcrumbs li"))
.filter((value, index) => index >= 1)
.map(listElement => listElement.getAttribute("title"))
.join(", ");
EDIT: I fix my answer, thanks to Barmar
something like that ?
const All_LI = [...document.querySelectorAll('#breadcrumb li')];
let a = ''
for (let i=1; i<All_LI.length; i++) { a += All_LI[i].title }
console.log('a-> ', a )
// .. or :
const b = All_LI.reduce((a,e,i)=>a+=(i>0)?e.title:'', '' )
console.log('b-> ', b )
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>

How can I keep the same index of the initially rendered list when users clicked on a filtered item in React Webpack?

I have included a filter (filteredCat) for my catalogue and it works as expected visually.
The issue is that although the items are filtered, the index being logged doesn't reflect the index of the new filtered list. It returns the index of the originally rendered list.
For example if the list is filtered down to a single item whose index was initially 10. It still logs the index of 0.
export default class Catalogue extends React.Component{
productClickHandler(index){
console.log(index);
};
render(){
let filteredCat = this.props.DVDLibrary.filter(
(dvds) => {
return(
dvds.title.toLowerCase().indexOf(this.props.searchFilterInput) !== -1
)
}
);
var catologueList = filteredCat.map((dvds,index) => {
return(
<li key={index} onClick={this.productClickHandler.bind(this,index)}>
<div className="inner">
<h2>{dvds.title}</h2>
<img src={dvds.image}/>
<p><strong>Rating</strong>: {dvds.rating}</p>
<p><strong>Price</strong>: {dvds.price}</p>
<p><strong>Stock</strong>: {dvds.stock}</p>
</div>
</li>
)
});
return(
<div>
<ul>
{catologueList}
</ul>
</div>
)
}
}
How can I keep the same index of the initially rendered list when users clicked on a filtered item?
Referencing the filteredCat variable instead of the catologueList index works.

ReactJS Different Classes

Iam new to ReactJS and I am working on a filterable gallery but now Iam looking at an example of thinking in ReactJS and I saw that they are building different classes for each component. I didn't do that but now Iam trying to do this, First my code looked like this: `
var SearchBar = React.createClass({
getInitialState() {
return { text:'', array: this.props.array};
},
handleChange(event) {
var array = this.filterList(event.target.value);
this.setState({ text: event.target.value, array: array });
return this.state.text;
},
render() {
var arrayComponents = this.state.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
return <div>
<h1>Hello, {this.props.name}</h1>
<p>{this.state.text}</p>
<input type="text" onChange={this.handleChange} />
<ul>
{arrayComponents}
</ul>
</div>;
},
filterList (filterText) {
var updatedList = this.props.array,
filterTextLength = filterText.length;
return updatedList.filter(function(item){
var splitName = item.name.toLowerCase().slice(0, filterTextLength);
var lowerCaseFilterText = filterText.toLowerCase();
return splitName === lowerCaseFilterText;
});
}
});
Now I want to create an other Class of ImageList which has to include the var arrayComponents but if i do this:
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
and than in the render function add <ImageList array={array}/> instead of <ul>{arrayComponent}</ul> it throws me the error that Cannot read property 'map' of undefined How do I pass the state of array into that ImageList class.
Here is a codepen: LINK
I've made the following changes in your code: http://codepen.io/PiotrBerebecki/pen/zKRAGZ
1) Pass the state of array into that ImageList class
<ImageList array={this.state.array} />
2) Add a return statement in the render method of ImageList
// Add return
return (
<ul>
{arrayComponents}
</ul>
);
3) Add key attribute to the li tag when using map method:
var arrayComponents = this.props.array.map(function(photo, index) {
// -------------------------------
// Add index to the li tag
// ----------------vvvvvvvvvvv
return <li key={index} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
React Docs: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
You need to pass the current state of the array. So your component declaration should look like this:
<ImageList array={this.state.array}/>
I glance three problems here.
First,
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
you should say:
return (<ul>
{arrayComponents}
</ul>)
Second, when you use dynamically generated codes, you should add a key prop to it:
var arrayComponents = this.props.array.map(function(photo) {
return <li key={SOME_KEY} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
Third, in your codepen when you say :
<ImageList array={array}/>
you are referencing the global variable array (you declare it on the top of your code), do you mean:
<ImageList array={this.state.array}/>

Unique "key" prop in React when returning list with multiple children

I have to render multiple lists in a react component. I create my lists in the following manner:
var jobResources = null;
if (this.state.jobResources) {
jobResources = _.map(this.state.jobResources, function(jobResource, i) {
return <ul><li key={i}>{jobResource.resource.name}</li><li key={Math.floor(Math.random() * (1000 - 100)) + 100}>{translations.resourceType[jobResource.resource.resourceType.name]}</li></ul>
})
}
When it comes to rendering, I render the list thus:
<div>
<h2>Who is Working on the Project?</h2>
{this.state.jobResources ? jobResources : null}
</div>
The resulting render is as follows:
<ul data-reactid=".0.2.0.2.1:0">
<li data-reactid=".0.2.0.2.1:0.$0">Mark Smith</li>
<li data-reactid=".0.2.0.2.1:0.$270">Hair & MakeUp</li>
</ul>
<ul data-reactid=".0.2.0.2.1:1">
<li data-reactid=".0.2.0.2.1:1.$1">John Doe</li>
<li data-reactid=".0.2.0.2.1:1.$377">Photographer</li>
</ul>
As far as I see it, the keys are unique. However, react gives me the following warning:
Warning: Each child in an array or iterator should have a unique "key"
prop. Check the render method of JobDetailPage. See
http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
for more information.
Could anyone tell me what is the mistake that I am making and what would be the correct way of going about this?
Never mind, figured it out. Apparently, the list element should have a key as well. The correct way of creating the list therefore would be:
var jobResources = null;
if (this.state.jobResources) {
jobResources = _.map(this.state.jobResources, function(jobResource) {
return <ul key={jobResource.id}><li key={jobResource.resource.name}>{jobResource.resource.name}</li><li key={jobResource.resource.resourceType.name}>{translations.resourceType[jobResource.resource.resourceType.name]}</li></ul>
})
}
I usually do this
if (this.state.jobResources) {
jobResources = _.map(this.state.jobResources, function(jobResource, i) {
return (
<ul>
<li key={i.toString()}>{jobResource.resource.name}</li>
<li key={`key${i}`}>
{
translations.resourceType[
jobResource.resource.resourceType.name
]
}
</li>
</ul>
);
});
}
This utility function from lodash/underscore solves it easily:
key={_.uniqueId()}

Categories