Remove item from array with click function - javascript

I have a Todo react app, where when you click on checkbox, the selected item is stored in an array, and displayed in a separate div where there is cross icon. I wanted to remove the displayed item in my second div when clicked on cross icon.
Below is my code:
const [list1, setList1] = useState([]);
const pushHandler1 = (data) => {
let newArr = [];
if (!list1.includes(data)) {
newArr = [...list1, data];
setList1(newArr)
console.log("put: ", newArr);
}
else {
newArr = list1.filter((id) => id !== data);
setList1(newArr)
console.log("pull", newArr);
}
}
<div id="listBox1">
<Checkbox
defaultChecked={false}
color="primary"
inputProps={{ 'aria-label': 'secondary checkbox' }}
onChange={(e) => pushHandler1(e.target.name)} name="Language1"
/>
<label>Language1</label>
</div>
<div id="listBox2">
{
list1.length > 0 &&
<div className='checkList'>
{
list1 && list1.length > 0 &&
list1.map((ele, index) => {
return (
<div key={index} className="listItemDiv">
<p>{ele}</p>
<i class="fa fa-times" aria-hidden="true" onClick={(e) => pushHandler1(e.target.name)} name="Language1"></i>
</div>
)
})
}
</div>
}
</div>
But instead of deleting this item, it creates a new empty div in my second Div. I am new to JavaScript and react js and not able to figure what I am doing wrong.
NOTE
Thank you for the answer:
changing the onClick in this way onClick={(e) => pushHandler1(ele)} helped me in solving the issue.
But now there is one more issue. When I cancel the selected item, my checkbox is still checked(true). So if now I uncheck it, it displays the unchecked item in my second div. Is there a way to also uncheck the checkbox, when cancelling the selected item?

i elements don't have a name attribute, and so they don't have the name reflected property that input and other elements have.
Since you're already creating a function for the click handler, you don't need to put an attribute on the i, just put the value directly in the handler:
<i class="fa fa-times" aria-hidden="true" onClick={(e) => pushHandler1("Language1")}></i>
That said, hardcoding the value doesn't look right. It seems like that should be ele, not "Language1":
<i class="fa fa-times" aria-hidden="true" onClick={(e) => pushHandler1(ele)}></i>

Related

Get key of mapped child component from parent function

I have a child navigation component and i'm trying to get the key of the selected item in the parent component to update my state to the selected nav item. I'm using a map in the child component and I cant seem to pass the key of the selected item back up to the parent item to set the state. here are my components
Parent
** my function to set the nav item **
let navPress = async key => {
let selNavItem = await navItems.find(object => {
return object.key === key
});
setActiveNavItem(selNavItem.key)
}
** return for parent component **
return(
<ProjectDetailNav
navItems={navItems}
activeNavItem={activeNavItem}
onClick={() => navPress}
/>
)
Child component (nav) with map function
<div id='nav-container'>
{props.navItems.map(navItem => {
if (navItem.key === props.activeNavItem) {
// console.log(navItem.key)
return (
<button className='active-nav-item' key={navItem.key} onClick={props.onClick(navItem.key)}>
<img className='nav-item-icon' src={navItem.icon} />
<h6 className='nav-item-title'>{navItem.title}</h6>
</button>
)
} else {
return (
<button className='nav-item' key={navItem.key} onClick={props.onClick(navItem.key)}>
<img className='nav-item-icon' src={navItem.icon} />
<h6 className='nav-item-title'>{navItem.title}</h6>
</button>
)
}
})}
</div >
By onClick={props.onClick(navItem.key)} you are assigning the return value of props.onClick to buttons click event calling it immediately on render. It should be wrapped in a function.
<button
className="active-nav-item"
key={navItem.key}
onClick={() => props.onClick(navItem.key)}
>
<img className="nav-item-icon" src={navItem.icon} />
<h6 className="nav-item-title">{navItem.title}</h6>
</button>
This way when onClick event is fired, the anonymous function will be called which will call props.onClick(navItem.key).

How to handle click on specific button in loop?

I have created following design:
Here is the react js code:
{this.props.BetSlipDataObj.betDetails.data.map(
(value, index) => {
<div className="d-flex justify-content-end">
<button
className="info-button"
data-toggle="collapse"
href="#collapseExample"
role="button"
aria-expanded="false"
aria-controls="collapseExample"
// onClick={
// value.betId != null
// ? _this.getBetIdDetails(value.betId)
// : value.childId != null
// ? _this.getBetIdDetails(value.childId)
// : ""
// }
>
</div>
})}
Here I am trying following task
If I click on one button it should expand the box
But if I click on one button all boxes are expanding.
If I call some method on click its getting called infinitely
Can someone help me to correct the code ?
You can call a function on button click. I think it was calling infinitely because you were not passing a reference to the function.
{this.props.BetSlipDataObj.betDetails.data.map(
(value, index) => (
<div className="d-flex justify-content-end" key={index}>
<button
className="info-button"
data-toggle="collapse"
href="#collapseExample"
role="button"
aria-expanded="false"
aria-controls="collapseExample"
onClick={
value.betId != null
? () => _this.getBetIdDetails(value.betId)
: value.childId != null
? () => _this.getBetIdDetails(value.childId)
: () => {}
}
>
</div>
)
)}
You were also missing the key prop on div
EDIT:
One button is opening all the boxes because they all have the same IDs and controls.
To make the IDs and controls unique, you can do something like this:
{this.props.BetSlipDataObj.betDetails.data.map(
(value, index) => (
<div className="d-flex justify-content-end" key={index}>
<button
className="info-button"
data-toggle={`collapse${index}`}
href={`#collapseExample${index}`}
role="button"
aria-expanded="false"
aria-controls={`collapseExample${index}`}
onClick={
value.betId != null
? () => _this.getBetIdDetails(value.betId)
: value.childId != null
? () => _this.getBetIdDetails(value.childId)
: () => {}
}
>
</div>
)
)}
The solution seems pretty straightforward. You're directly executing a function in the onClick. That's why all of them are getting executed. You just need to change the onClick as follows:
onClick = { () => {
if(value.betId != null){
_this.getBetIdDetails(value.betId)
} else if (value.childId != null){
_this.getBetIdDetails(value.childId)
} else {return ""} }
To each div you need to give unique id, may be primary key of that row, and when that button is clicked pass the id to the opening function, and append that to id of that div to open. Hope it helps. This will go like this:-
onclick=handler(1)
div id should be like this:- open1, open2
handler should be like this:-
handler(id) => idofelement+id open

in react when I remove a dynamic input, the input does not show the right value on the browser

I'm new in React and i'm learning to use it with hooks.
I tried to put dynamics inputs which works so far but I have a display problem.
If I delete the last input, no problem but if I delete others inputs than the last one then the correct values does not show on the browser.
For example I add 2 inputs.
first one titled "one" and second titled "two".
If I delete "one", the remain input on screen shows "one" or it should show "two".
However, in the array where I collect the inputs infos, there is the correct remaining input.
(see screenshot).
How can I do to show the correct title in the input on the browser ?
const [titleSelected, setTitleSelected] = useState(true);
const [questionType, setQuestionType] = useState([]);
const [questionsTitle, setQuestionsTitle] = useState([]);
{questionType ? (
questionType.map((questions, index) => {
return (
<div key={index} className="questions">
<div className={questions === "texte" ? "orange" : "red"}>
<span>{questions === "texte" ? "1" : "2"}</span>
<img src={Minus} alt="" />
<img
src={questions === "texte" ? FileWhite : StarWhite}
alt=""
/>
</div>
<input
type="text"
placeholder="Ecrivez votre question"
onChange={(event) => {
let tab = [...questionsTitle];
// si index de l'objet existe on update index de l'objet par index sinon on push le nouvel objet
let tabIndex = tab.findIndex(
(element) => element.index === index
);
if (tabIndex !== -1) {
tab[tabIndex].type = questionType[index];
tab[tabIndex].title = event.target.value;
} else {
tab.push({
index: index,
type: questionType[index],
title: event.target.value,
});
}
setQuestionsTitle(tab);
}}
></input>
<div>
<img src={ChevronUp} alt="move up" />
<img src={ChevronDown} alt="move down" />
<img
src={SmallTrash}
alt="delete question"
onClick={() => {
let tab = [...questionType];
tab.splice(index, 1);
setQuestionType(tab);
let tabTitle = [...questionsTitle];
tabTitle.splice(index, 1);
setQuestionsTitle(tabTitle);
}}
/>
</div>
</div>
);
})
) : (
<div></div>
)}
<div className="questionType">
<div
className="addText"
onClick={() => {
let tab = [...questionType];
tab.push("texte");
setQuestionType(tab);
}}
>
<img src={File} alt="" />
<p>Ajouter une question "Texte"</p>
</div>
<div
className="addNote"
onClick={() => {
let tab = [...questionType];
tab.push("note");
setQuestionType(tab);
}}
>
<img src={Star} alt="" />
<p>Ajouter une question "Note"</p>
</div>
</div>
screenshot
Issues
You are mutating the array you are mapping so don't use the array index as the react key. If you remove the ith element all the elements shift up, but the keys remain unchanged and react bails on rerendering.
Lists & Keys
questionType.map((questions, index) => (
<div key={index} className="questions">
...
</div>
)
The array index as a React key doesn't "stick" to the element object it "identifies".
You've also some state object mutation occurring.
tab[tabIndex].type = questionType[index];
tab[tabIndex].title = event.target.value;
This is masked by the shallow copy let tab = [...questionsTitle];.
Solution
Select a react key that is unique among siblings and related to the elements, like an id.
Since you enclose the index when adding new elements to the array I think you can resolve the key issue by simply using the saved index.
questionType.map((questions, index) => (
<div key={questionsTitle[index].index} className="questions">
...
</div>
)
This may be a little confusing so I suggest changing the property to id.
questionType.map((questions, index) => (
<div key={questionsTitle[index].id} className="questions">
...
<input
...
onChange={event => {
...
tab.push({
id: index,
type: questionType[index],
title: event.target.value,
});
...
}}
/>
...
</div>
)
A further suggestion is to avoid using the array index at all. The following code can quickly get out of sync when the array index being mapped doesn't align to the saved index in the element.
let tabIndex = tab.findIndex(element => element.index === index);
Generate id's for your elements and use that to determine if elements should be updated or appended. When updating make sure to shallow copy the array and then also shallow copy the element object being updated.

key parameter issue in ReactJS Hooks, updating all Products items when using (add/subtract) indivisually

I am kind of confused when applying the key parameter to the below code in ReactJS, i understand we need to use the key parameter to uniquely identify each DOM element, when i tried to use key={index} in buttons it comes up with error(Encountered two children with the same key).
I am also a bit puzzled here, as updating the setQuantity update all the items in all products(as shown in screenshot below), not sure how i can use the unique key item to update each product quantity only.
Any help appreciated.
Update 1: the code is now updated with key={productName}, but still the adding/ subtracting value is updating across all products when adding/subtracting single product
function Home({ props }) {
//quantitiyselected, to set the no. of items purchased
const [quantitiyselected, setQuantity] = useState(0);
// below function to add, subtract quantity
let selectquantity = (e) => {
if (e.currentTarget.name == "add") {
let i = quantitiyselected + 1;
setQuantity(i);
} else if (e.currentTarget.name == "subtract" && quantitiyselected > 0) {
let z = quantitiyselected - 1;
setQuantity(z);
} else;
};
return (
<div className="products">
{props.map((eachproduct, index) => {
let productName = eachproduct.product_name;
let producNumber = eachproduct.producNumber;
let price = eachproduct.price;
let desc = eachproduct.productDescription;
let photo = eachproduct.image_URL;
let stockQuantity = eachproduct.stockQuantity;
return (
<div className="products" key={productName }>
<ul>
<li>
<img className="products-image" src={photo} />
</li>
<li>{productName} </li>
<li>
Item No:{producNumber}(InStock:{stockQuantity})
</li>
<li>price:{price}£ </li>
<li>{desc}</li>
<li>
<ButtonGroup aria-label="quantityofproduct">
<Button
variant="secondary"
name="subtract"
value="subtract"
onClick={selectquantity}
>
-
</Button>
<Button variant="secondary">
{quantitiyselected}
</Button>
<Button
variant="secondary"
name="add"
value="add"
onClick={selectquantity}
>
+
</Button>
</ButtonGroup>
variant="primary">
Buy
</Button>
</li>
</ul>
</div>
);
})}
</div>
);
}
export default Home;
You are applying key={index} to multiple elements in your render (<div>, <ul> and each <Button />), but it only needs to go on the top level element that is being rendered in the array which in your case is the <div className="products">.
As an aside, an array index is generally not a good candidate for the key - it should be unique to each item that is being rendered. In your example, I think producNumber would be the best bet as I am assuming it is some unique identifier to each product.
remove key={index} from everything that you have it on
add key={producNumber} to the inner <div className="products">
This is a good resource to learn more about React's key prop.
The reason every product has the same quantity, is you only have a single number for your state:
React.useState(0);
You are trying to track quantities per product, so you would need a quantity for each product. One way you could do that is by using an object ({}) that has properties of your producNum and quantity as their value. For example:
const products = [
{ name: "thing", num: 152 },
{ name: "other-thing", num: 254 },
];
const initialQuantities = products.reduce(
(quantities, product) => ({ ...quantities, [product.num]: 0 }),
{}
);
console.log(initialQuantities);
This creates the object with each product with an initial quantity of 0.
When you then increase or decrease the quantity, you would pass the product number of the product you clicked to the function, and then you can set the new quantity of just that product, while leaving the other product quantities untouched by using Spread syntax (...):
const initialQuantities = products.reduce(
(quantities, product) => ({ ...quantities, [product.producNumber]: 0 }),
{}
);
const [quantities, setQuantities] = React.useState(initialQuantities);
const increase = (productNum) => {
setQuantities({
...quantities,
[productNum]: quantities[productNum] + 1,
});
};
const decrease = (productNum) => {
setQuantities({
...quantities,
[productNum]: Math.max(0, quantities[productNum] - 1),
});
};
and then your onClick would become:
onClick={() => increase(producNumber)}
and
onClick={() => decrease(producNumber)}
A simple implementation can be seen here: https://codesandbox.io/s/icy-dust-nbetq?file=/src/App.js:175-655

Content Editable = true, but only takes one value at a time

I'm working on a simple project where I need to make an item in a list editable and then update a JS item to store this.
I'm using Content Editable = True, and it works when I comment out my handleItemEdit function, but when I turn it on, I can only insert one character at a time, forcing me to keep clicking to edit.
Clearly this problem stems from my function, but I can't seem to figure out why.
//Responsible for listening for an edit and updating my object with the new text.
function handleEditItem() {
$('.js-shopping-item').on('input', function(event) {
const itemIndex = getItemIndexFromElement(event.currentTarget); //assigning the index of the the editted item to itemIndex
const updatedItem = STORE.items[itemIndex];
updatedItem.name = event.currentTarget.innerHTML;
renderShoppingList();
});
}
//Returns the index of an Item in the Store
function getItemIndexFromElement(item) {
const itemIndexString = $(item)
.closest('.js-item-index-element')
.attr('data-item-index');
return parseInt(itemIndexString, 10);
}
//Function responsible for returning the template HTHML to insert into the html.
function generateItemElement(item) {
let itemIndex = STORE.items.indexOf(item);
return `
<li class="js-item-index-element" data-item-index="${itemIndex}">
<span contentEditable='true' class="shopping-item js-shopping-item ${item.checked ? 'shopping-item__checked' : ''}">${item.name}</span>
<div class="shopping-item-controls">
<button class="shopping-item-toggle js-item-toggle">
<span class="button-label">check</span>
</button>
<button class="shopping-item-delete js-item-delete">
<span class="button-label">delete</span>
</button>
</div>
</li>`;
}

Categories

Resources