I'm currently having an issue where when I click on the delete button for a particular element it only deletes the first item in list array every time rather than the selected one.
Any help with this would be greatly appreciated! :)
import "./css/App.css";
import React, { useRef } from "react";
function App() {
const [item, setItem] = React.useState("");
const [list, updateList] = React.useState([]);
const toDoItem = (item) => {
let newItem = { text: item.target.value, key: Date.now() };
setItem(newItem);
};
const addItem = () => {
updateList((prevState) => [...list, item]);
document.getElementById("Input").value = "";
};
const deleteItem = (index) => {
updateList((prevState) => {
let items = [...prevState];
console.log(items);
items.splice(index, 1);
return items;
});
};
return (
<div className="App">
<h2>Type a todo item you want to complete</h2>
<input
type="text"
placeholder="Add a todo..."
id="Input"
onChange={toDoItem}
/>
<button id="Add" onClick={addItem}>
Add
</button>
{list.map((item, index) => {
return (
<ol key={index}>
{item.text[0].toUpperCase() + item.text.slice(1).toLowerCase()}
<button>Edit</button>
<button id="Delete" onClick={deleteItem}>
✗
</button>
{/* <button onClick={test}>test</button> */}
</ol>
);
})}
</div>
);
}
export default App;
You forgot pass index when call deleteItem
onClick={() => deleteItem(index)}
Related
I was creating the functionality of pinning and unpinning of particular note, so when the user clicks the thumbtack icon I want that icon of only that particular note changes to a cross icon but when I am clicking on the second notes to pin it then the icon that changed on previous pinned note gets restored to its original form.
I have created the pinning functionality using onPin function but struggling with changing the icon of that particular pinned item.
I want to add icons to pinned items in such a way that previously added close icons stay in their place and do not get updated.
What I tried?
So i created the state variable iconId which is an array so whenever the user clicks pinned icon then new id will be pushed to the iconId array and while displaying the output I put the condition that if the current id is included in iconId array then change icon of all those respective ids in iconId to cross icon, apparently this functionality dint work.
-----------------------App.js--------------------------------
import React, { useState } from "react";
import './App.css';
import Input from './Components/Input';
import Navbar from './Components/Navbar';
import Notesview from './Components/Notesview';
import Notesdata from "./Data/Notesdata";
function App() {
const [data, setData] = useState(Notesdata);
// const [pin, setpin] = useState(true)
const [iconId, seticonId] = useState([])
function handleDelete(id) {
let newData = data.filter((item) => item.id !== id)
setData(newData)
console.log(newData)
console.log(Notesdata)
console.log(0)
}
function handlePost(value) {
// Notesdata.push(value)
// setData(Notesdata)
// // console.log(typeof data)
// console.log(Notesdata)
setData([...data, value]);
}
function onPin(id) {
let index = data.map((item) => {
return item.id
}).indexOf(id)
let arr1 = data.slice(0, index).concat(data.slice(index + 1))
arr1.unshift(data[index])
setData(arr1);
seticonId([...iconId] , id)
console.log(iconId)
}
function handleclose() {
// setpin(!pin)
// seticonId("")
}
return (
<div className="App">
<header className="App-header">
<Navbar />
<Input data={data} handlePost={(value) => handlePost(value)} />
<Notesview handleDelete={handleDelete} Data={data} onPin={onPin} iconId={iconId} handleclose={handleclose} />
</header>
</div>
);
}
export default App;
----------------Noteview function(mapping function)---------------
import React from 'react'
import Notescard from './Notescard'
import "../Styles/Notes.css"
// import { useState } from 'react'
const Notesview = ({ Data, handleDelete, onPin , iconId, handleclose}) => {
return (
<>
<div className='notes'>
{Data && Data.map((item) => {
return <Notescard item={item} handleDelete={handleDelete} onPin={onPin} iconId={iconId} key={item.id} handleclose={handleclose}/>
})
}
</div>
</>
)
}
export default Notesview
-----------------------------Notescard component------------------
import React from "react";
import "../Styles/Notescard.css";
import { FaThumbtack, FaTrashAlt, FaPencilAlt ,FaTimesCircle} from "react-icons/fa";
// import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Notescard = ({ item , handleDelete,onPin,iconId,handleclose, key}) => {
return (
<>
<div className="box">
<div className="content">
<h2 className="item1">{item.title}</h2>
<h4 className="item1"> {item.tagline}</h4>
<p className="item2">{item.description}</p>
</div>
<div className="icons">
{iconId.includes(item.id) ? <FaTimesCircle onClick={handleclose}/> : <FaThumbtack id={item.id} onClick={() => onPin(item.id)}/> }
<FaTrashAlt onClick={() => handleDelete(item.id)}/>
<FaPencilAlt />
</div>
</div>
</>
);
};
export default Notescard;
Issue
You are passing two arguments to the seticonId state updater function.
seticonId([...iconId], id)
The id is never added to the iconId state.
Solution
Use a functional state update to append the id to the array.
seticonId((iconId) => iconId.concat(id));
Code:
const Notescard = ({ item, handleDelete, onPin, iconId, handleclose }) => {
return (
<div className="box">
<div className="content">
<h2 className="item1">{item.title}</h2>
<h4 className="item1"> {item.tagline}</h4>
<p className="item2">{item.description}</p>
</div>
<div className="icons">
{iconId.includes(item.id) ? (
<FaTimesCircle onClick={() => handleclose(item.id)} />
) : (
<FaThumbtack id={item.id} onClick={() => onPin(item.id)} />
)}
<FaTrashAlt onClick={() => handleDelete(item.id)} />
<FaPencilAlt />
</div>
</div>
);
};
...
const Notesview = ({ Data, handleDelete, onPin, iconId, handleclose }) => {
return (
<div className="notes">
{Data.map((item) => {
return (
<Notescard
item={item}
handleDelete={handleDelete}
onPin={onPin}
iconId={iconId}
key={item.id}
handleclose={handleclose}
/>
);
})}
</div>
);
};
...
export default function App() {
const [data, setData] = useState(Notesdata);
const [iconId, seticonId] = useState([]);
function handleDelete(id) {
let newData = data.filter((item) => item.id !== id);
setData(newData);
console.log(newData);
console.log(Notesdata);
console.log(0);
}
function handlePost(value) {
setData([...data, value]);
}
function onPin(id) {
setData((data) => {
const index = data.findIndex((item) => item.id === id);
const arr1 = data.slice(0, index).concat(data.slice(index + 1));
arr1.unshift(data[index]);
return arr1;
});
seticonId((iconId) => iconId.concat(id));
}
function handleclose(id) {
setData((data) => {
const index = data.findIndex((item) => item.id === id);
const insertIndex = data.findIndex((item) => !iconId.includes(item.id));
const arr1 = data.slice(0, index).concat(data.slice(index + 1));
arr1.splice(insertIndex - 1, 0, data[index]);
return arr1;
});
seticonId((iconId) => iconId.filter((elId) => elId !== id));
}
return (
<div className="App">
<Input data={data} handlePost={(value) => handlePost(value)} />
<Notesview
handleDelete={handleDelete}
Data={data}
onPin={onPin}
iconId={iconId}
handleclose={handleclose}
/>
</div>
);
}
I'm trying to splice an array hook State, so I can remove items from my list. I know you're not supposed to modify any hook states directly, so I made a copy into array2 before settting it, but it's removing all items below the one I click, instead of just the one I click. Any ideas?
export default function Shopping() {
const [item, setItem] = useState('Default');
const [list, setList] = useState([]);
const [array, setArray] = useState([]);
let array2 = [];
const name = ({ target }) => setItem(target.value);
const remove = ({ target }) => {
for (let x = 0; x <= array.length; x++) {
if (array[x] === target.value) {
array2 = list;
array2 = array2.splice(x, 1);
}
setList([...list, array2]);
}
};
const create = () => {
if (item !== 'Default' && document.getElementById('box').value !== '') {
let value = document.getElementById('box').value;
setArray([...array, value]);
setList([
...list,
<div>
<p>
{' '}
{item} Qty: 1<button>+</button>
<button>-</button>
<button
value={document.getElementById('box').value}
onClick={remove}
>
x
</button>
</p>
</div>,
]);
} else {
alert('Please enter an Item');
}
document.getElementById('box').value = '';
setItem('Default');
};
const clear = () => setList([]);
return (
<div>
<input type='textbox' id='box' onChange={name} />
<input type='button' value='Add' onClick={create} />
<input type='button' value='Clear' onClick={clear} />
{list}
</div>
);
}
You should just store the strings in the list array and display the elements using a map function:
export default function Shopping() {
const [item, setItem] = useState('Default');
const [list, setList] = useState([]);
const name = ({ target }) => setItem(target.value);
const remove = (index) => {
const filteredList = [...list].splice(index, 1);
setList(filteredList);
};
const create = () => {
if (item !== 'Default' && item !== '') {
setList([...list, item]);
} else {
alert('Please enter an Item');
}
setItem('Default');
};
const clear = () => setList([]);
return (
<div>
<input type='textbox' id='box' onChange={name} />
<input type='button' value='Add' onClick={() => create()} />
<input type='button' value='Clear' onClick={() => clear()} />
{list.map((it, index) => {
return (
<div key={index}>
<p>
{' '}
{it} Qty: 1<button>+</button>
<button>-</button>
<button value={it} onClick={() => remove(index)}>
x
</button>
</p>
</div>
);
})}
</div>
);
}
```
Just Update the code in remove function because array2 = list also copies the reference of list array so when you update the array2 it also update the list array.so replace the remove function code with given code below .
if ((array[x])===target.value){
array2 = list.map(item=>item)
array2 = array2.splice(x,1)
}
I am trying to capitalize the product name after fetching if from a products list using a button.
Basically, a button fetches the data. Which gives us the product name as a heading with a CLEAR and CAPITALIZE button with each item.
Clear button removes the item from the list. Implemented.
Capitalize button Capitalizes the product name.
Not able to figure out how to implement the capitalize function.
CODE::
capHandler function implements the functionality.
import React, { useRef, useState } from 'react';
import axios from 'axios';
function App(){
const url = 'https://course-api.com/react-store-products';
const [products, setProducts] = useState([]);
const productNameRef = useRef();
const fetchData = async () => {
const { data } = await axios.get(url);
setProducts(data);
};
const clearProducts = () => {
setProducts([]);
};
const clearSingleProduct = (productID) => {
const filteredArray = products.filter((item) => item.id != productID);
setProducts(filteredArray);
};
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function capHandler() {
console.log('Single Word Capitalized');
}
return (
<>
<button onClick={fetchData}>Fetch Products</button>
<button onClick={clearProducts}>Clear Products</button>
<div>
{products.map((product) => {
return (
<div ref={productNameRef} id={product.id} key={product.id}>
<h3 className="productName">{product.name}</h3>
<button onClick={() => clearSingleProduct(product.id)}>
Clear
</button>
<button onClick={capHandler}>Capitalize</button>
</div>
);
})}
</div>
</>
);
};
export default App;
you make the capHandler accept product index id and update the state.
function capHandler(id) {
const newProducts = [...products]
newProducts[i].name = capitalizeFirstLetter(newProducts[i].name)
setProducts([...newProducts]);
}
Also need to pass the map index while mapping the products
<div>
{products.map((product, i) => {
return (
...
Lastly call the fucntion in the button
<button onClick={() => capHandler(i)}>Capitalize</button>
Update the capHandler() function with the following body:
function capHandler() {
setProducts(products.map(i => ({...i , name: capitalizeFirstLetter(i.name)}))
}
So I was trying to update the value I got by the Addlist and I tried this but this isn;t working. Also when I click on the '+' button without writing anything, an empty list is created. How should I stop it. I've attached a code below.
import React from "react";
import "./App.css";
import { useState } from "react";
import TodoList from "./components/TodoList";
function App() {
const [input, setInput] = useState("");
const [list, setList] = useState([]);
const updateList = (e) => {
setInput(e.target.value);
};
const AddList = () => {
console.log("value added")
setList((addValue) => {
return [...addValue, input];
});
setInput("");
};
const updateItems=(id)=>{
const newValue=[...list].map((newVal)=>{
if(input.id===id){
input.text='';
}
return newVal;
})
setList(newValue);
}
const deleteItems = (id) => {
console.log("deleted");
setList((addValue) => {
return addValue.filter((element, index) => {
return index !== id;
});
});
};
return (
<div className="todo-app">
<h1> Enter Anything</h1>
<input
type="text"
placeholder="Add anything"
value={input}
onChange={updateList}
/>
<button onClick={AddList}>+</button>
<ul>
{list.map((itemsvalue, id) => {
return (
<TodoList
itemsValue={itemsvalue}
key={id}
onSelect={deleteItems}
id={id}
onUpdate={updateItems}
/>
);
})}
</ul>
</div>
);
}
export default App;
Any kind of help would be appreciated. Also if I want to split this into multiple components is there a way to do.
When user clicks on the add button there is the check for empty String AddList method
for ex:- User updates second index value, second position value will get updated.
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [index, setIndex] = useState(null);
const updateList = (e) => {
setInput(e.target.value);
};
useEffect(() => {
setList(list);
console.log(list, '<>?');
}, [index]);
const AddList = () => {
if (input.trim() !== '') {
setList([...list, input]);
}
setInput('');
};
const updateValue = (index) => {
console.log(list[index]);
setIndex(index);
if (list[index].trim() !== '') {
setInput(list[index]);
}
};
const UpdateList = () => {
list[index] = input;
console.log(list, 'before <>?');
setIndex(null);
setInput('');
};
return (
<div>
<input type="text" placeholder="Add anything" value={input} onChange={updateList} />
<button disabled={!index && !list.length === 0} onClick={AddList}>
Add
</button>
<button disabled={input.trim() === ''} onClick={UpdateList}>
Update
</button>
{list.map((m, index) => (
<h1 style={{ border: '1px solid black' }} onClick={() => updateValue(index)}>
{m}
</h1>
))}
</div>
);
Actually, I am stuck at a point, Please have a look the code once.
I want to push the user input into my array. Can anyone explain why it's throwing error.
import React, { useState } from 'react';
function Cart() {
const [item, setItem] = useState({ cart: ['Corn', 'Potato'] });
const saveInput = (e) => {
setItem({ input: e.target.value });
};
const addNewItem = () => {
const { cart, input } = item;
cart.push(input);
return setItem({ cart: cart });
};
return (
<div>
<input type="text" onChange={saveInput} />
<button onClick={addNewItem}>Add Item</button>
<ol>
{item.cart.map((subItems, sIndex) => {
return <li key={sIndex}> {subItems}</li>;
})}
</ol>
</div>
);
}
export default Cart;
You can use separate states. One for handle input and another for handle list:
import React, { useState } from "react";
function Cart() {
const [item, setItem] = useState(["Corn", "Potato"]);
const [input, setInput] = useState("");
const saveInput = (e) => {
setInput(e.target.value);
};
const addNewItem = () => {
const copyCart = [...item];
copyCart.push(input);
setItem(copyCart);
setInput("");
};
return (
<div>
<input value={input} type="text" onChange={saveInput} />
<button onClick={addNewItem}>Add Item</button>
<ol>
{item.map((subItems, sIndex) => {
return <li key={sIndex}> {subItems}</li>;
})}
</ol>
</div>
);
}
export default function App() {
return <Cart />;
}
Here's the full example:
Just like Dominik said, it would be much more efficient to have separate of your state.
If you want to make your existing code works:
Change
setItem({ input: e.target.value });
TO:
setItem({ ...item, input: e.target.value });
You forgot to spread your previous state before changing new ones.
import React, { useState } from "react";
function Cart() {
const [item, setItem] = useState({ cart: ["Corn", "Potato"] });
// have a new state to hold the input value
const [inputValue, setInputValue] = useState("");
const saveInput = (e) => {
setInputValue(e.target.value);
};
const addNewItem = () => {
// spread the exisiting cardlist and append the newly added input value
setItem((existingCartList) => ({
cart: [...existingCartList.cart, inputValue]
}));
// clear the input once it is added to card list
// so we can prevent the user from manually clearing it
setInputValue("");
};
return (
<div>
{/* need to pass the inputValue state to the value attribute
which makes the input a controlled component */}
<input type="text" value={inputValue} onChange={saveInput} />
<button onClick={addNewItem}>Add Item</button>
<ol>
{item.cart.map((subItems, sIndex) => {
return <li key={sIndex}> {subItems}</li>;
})}
</ol>
</div>
);
}
export default Cart;
Working Sandbox
Reference
Controlled Components