react
I wish clicking a button would reduce an item, but clicking a button would do nothing ... please help me...
const [cars, setCars] = useCars([])
const handleDelete = id => {
const remaining = cars.filter(car => car._id !== id);
setCars(' ');
}
please check this i have written sample code
import React, { useState } from 'react';
export default function App() {
const [lists, setLists] = useState([]);
const remove = (index) => {
// REMOVING THE RECORD BASED ON THE PREVIOUS STATE VALUE - ON THE GIVEN INDEX
setLists((v) => {
const a = [...v];
a.splice(index, 1);
return [...a];
});
};
// FOR ADDING SAMPLE DATA
const addList = () => {
setLists((v) => [...v, { name: 'List data ' + lists.length }]);
};
return (
<div>
<button onClick={addList}>Add List</button>
<ul>
{lists.map((e, k) => (
<li key={k}>
{e.name} <button onClick={(k) => remove(k)}>Remove</button>{' '}
</li>
))}
</ul>
</div>
);
}
Related
I have a problem with the localStorage in my application. When I add items to a list of "favorites" they are stored without any problem in the localStorage, they can even be deleted by clicking them again.
But when I refresh the page, my application doesn't read that these items are in the favorites list and therefore doesn't mark them. Also, when I add a new item to the favorites list it causes it to delete everything from localStorage and start over.
Here's a gif of the localStorage view
Here's the code:
import React, { useState, useEffect } from 'react';
import SearchBar from '../../SearchBar/SearchBar.js';
import FiltersBox from '../FiltersBox/FiltersBox.js';
import { getItems } from '../../../Database/Database.js';
import './ItemsContainer.css';
function ItemsContainer() {
const [items, setItems] = useState([]);
const [search, setSearch] = useState('');
const [favoriteItems, setFavoriteItems] = useState([]);
let localItems = localStorage.getItem('Favorite Items');
const [sortPrice, setSortPrice] = useState('');
const [filterCategory, setFilterCategory] = useState('');
const addItemToFavorites = item => {
let existentItem = favoriteItems.find(favItem => favItem.id === item.id);
if (existentItem) {
let filterTheExistentItem = favoriteItems.filter(
favItem => item.title !== favItem.title
);
setFavoriteItems(filterTheExistentItem);
let stringItems = JSON.stringify(filterTheExistentItem);
localStorage.setItem('Favorite Items', stringItems);
} else {
setFavoriteItems([...favoriteItems, item]);
let stringItems = JSON.stringify([...favoriteItems, item]);
localStorage.setItem('Favorite Items', stringItems);
}
};
const filteredItemsList = () => {
let newItemList = [];
newItemList = items.filter(item => {
if (filterCategory !== '' && filterCategory !== 'none') {
return item.category === filterCategory;
} else {
return item;
}
});
if (sortPrice === 'ascending') {
return newItemList.sort((a, b) => (a.price > b.price ? 1 : -1));
} else if (sortPrice === 'descending') {
return newItemList.sort((a, b) => (b.price > a.price ? 1 : -1));
} else {
return newItemList;
}
};
function onSortSelected(sortValue) {
setSortPrice(sortValue);
}
function onCategorySelected(categoryValue) {
setFilterCategory(categoryValue);
}
useEffect(() => {
getItems().then(res => setItems(res));
}, []);
useEffect(() => {
let xd = JSON.parse(localItems);
console.log(xd);
}, [localItems]);
return (
<div>
<SearchBar setSearch={setSearch} />
<FiltersBox
items={items}
setItems={setItems}
onSortSelected={onSortSelected}
onCategorySelected={onCategorySelected}
/>
<div>
{filteredItemsList()
.filter(item =>
search.toLowerCase() === ''
? item
: item.title.toLowerCase().includes(search)
)
.map(item => (
<div key={item.id}>
<div>{item.title}</div>
<button
className={favoriteItems.includes(item) ? 'si' : 'no'}
onClick={() => addItemToFavorites(item)}>
Add to favorites
</button>
</div>
))}
</div>
</div>
);
}
export default ItemsContainer;
And here I leave a GIF with a continuous console.log of the localStorage:
I tried everyrhing, and I don't know what is happening.
You're retrieving your items in localItems and... you do nothing with this variable. You should initialize your state favoritesItems with your local storage
const getItemsFromLocalStorage = () => {
const items = localStorage.getItem('Favorite Items');
return items ? JSON.parse(items) : [];
}
const [favoriteItems, setFavoriteItems] = useState(getItemsFromLocalStorage())
This is where the culprit is:
const [favoriteItems, setFavoriteItems] = useState([]);
let localItems = localStorage.getItem('Favorite Items');
You load localStorage into localItems, but you expect it to be in favoriteItems, where you have never assigned it. You would need to specify the item of localStorage as the initial state, like:
let localItems = localStorage.getItem('Favorite Items');
const [favoriteItems, setFavoriteItems] = useState(localItems ? localItems : []);
I'm making it so that every component is one element (button, the whole list, a single element...) I'm having trouble figuring out how to make my list print below the form. Tasks are shown in console.log() but I can't seem to get the right data transferred.
Thanks in advance for any help
This is items.jsx code
import React, { useState} from 'react'
import './todo.css'
import List from './list'
import Button from './button';
function Items () {
const [tasks, setTasks] = useState([]);
const [value, setvalue] = useState("");
/* const onChange = (e) => {
setvalue(e.target.value)
// console.log('type')
} */
const onAddTask = (e) =>{
e.preventDefault();
console.log('submit')
const obj = {
name: value ,
id: Date.now(),
};
if (value !== "") {
setTasks(tasks.concat(obj));
setvalue("")
console.log(obj)
}
};
return(
<div className="form">
<header>Your todo list</header>
<input
placeholder="type your task"
value={value}
onChange={(e) => setvalue(e.target.value)}/>
<input type="date" placeholder='Set your date!'/>
<button onClick={onAddTask}>Submit task</button>
<List data = {List}/>
</div>
)
}
export default Items
This is list.jsx code
import React , { useState } from "react";
import "./Items"
import Button from "./button"
const List = (tasks) => {
return(
<div>
{tasks.map}
</div>
)
console.log(task.map)
}
export default List
step 1
Here's a fully functioning demo to get you started -
function Todo() {
const [items, setItems] = React.useState([])
const [value, setValue] = React.useState("")
const addItem = event =>
setItems([...items, { id: Date.now(), value, done: false }])
return <div>
<List items={items} />
<input value={value} onChange={e => setValue(e.target.value)} />
<button type="button" onClick={addItem}>Add</button>
</div>
}
function List({ items = [] }) {
return <ul>
{items.map(item =>
<ListItem key={item.id} item={item} />
)}
</ul>
}
function ListItem({ item = {} }) {
return <li>{item.value}</li>
}
ReactDOM.render(<Todo />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
start with good state
Note using an Array to store the items is somewhat inefficient for the kinds of operations you will need to perform. Consider using a Map instead. Run the demo again and click on a list item to toggle its state -
const update = (m, key, func) =>
new Map(m).set(key, func(m.get(key)))
function Todo() {
const [items, setItems] = React.useState(new Map)
const [value, setValue] = React.useState("")
const addItem = event => {
const id = Date.now()
setItems(update(items, id, _ => ({ id, value, done: false })))
}
const toggleItem = id => event =>
setItems(update(items, id, item => ({ ...item, done: !item.done })))
return <div>
<List items={items} onClick={toggleItem} />
<input value={value} onChange={e => setValue(e.target.value)} />
<button type="button" onClick={addItem}>Add</button>
</div>
}
function List({ items = new Map, onClick }) {
return <ul>
{Array.from(items.values(), item =>
<ListItem key={item.id} item={item} onClick={onClick(item.id)} />
)}
</ul>
}
function ListItem({ item = {}, onClick }) {
return <li onClick={onClick}>
{ item.done
? <s>{item.value}</s>
: item.value
}
</li>
}
ReactDOM.render(<Todo />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
do more with less
Functional programming goes a long way in React. Using a curried update function we can take advantage of React's functional updates -
const update = (key, func) => m => // <-
new Map(m).set(key, func(m.get(key)))
function Todo() {
// ...
const addItem = event => {
const id = Date.now()
setItems(update(id, _ => ({ id, value, done: false }))) // <-
}
const toggleItem = id => event =>
setItems(update(id, item => ({ ...item, done: !item.done }))) // <-
// ...
}
but don't stop there
Avoid creating the todo item data by hand { id: ..., value: ..., done: ... }. Instead let's make an immutable TodoItem class to represent our data. A class also gives us an appropriate container for functions that would operate on our new data type -
class TodoItem {
constructor(id = 0, value = "", done = false) {
this.id = id
this.value = value
this.done = done
}
toggle() {
return new TodoItem(id, value, !this.done) // <- *new* data
}
}
Now our Todo component is unmistakable with its intentions -
function Todo() {
// ...
const [items, setItems] = useState(new Map)
const addItem = event => {
const id = Date.now()
setItems(update(id, _ => new TodoItem(id, value))) // <- new TodoItem
}
const toggleItem = id => event =>
setItems(update(id, item => item.toggle())) // <- item.toggle
// ...
}
When the user clicks the plus button, I want the task array to appear as a list. I am not able to push my tasks to an array
import logo from './logo.svg';
import './App.css';
import { BsPlusLg } from "react-icons/bs";
import { useState } from 'react';
const App = () => {
const items = ["Pizza", "Burger", "Shawarma", "Biryani", "Butter Naan", "Panner", "Chapathi"];
let tasks = [];
const [searchValue, setSearchValue] = useState("");
const changeValue = (event) => {
setSearchValue(event.target.value);
}
const searchedItems = tasks.filter((item) => {
if(item.toLowerCase().includes(searchValue.toLowerCase())){
return item;
}
})
const handleClick = () => {
if(items.length>0){
tasks.push(items.pop());
}
}
On clicking the plus button, after the items are added to the tasks array, they should appear as a list.
return (
<div className="App">
<div className='navbar'>
<input type="text" value={ searchValue } onChange={ changeValue } placeholder="Search"></input>
{/* <div className="verticalLine"></div> */}
<button onClick={ handleClick }><BsPlusLg /></button>
</div>
<hr/>
<div className='list'>
<ul>
{(searchValue.length>0)&&(searchedItems.map((item) => {
return <li key={item}>{item}</li>}))
}
{(searchValue.length===0)&&(tasks.map((item) => {
return <li key={item}>{item}</li>
}))
}
</ul>
</div>
</div>
);
}
export default App;
The elements in the tasks in the page are not being displayed when I log out of the tasks that are being pushed.
Clicking on your button does not update your component and will therefore rerender your list.
If if it would, it will run the whole function again and even call let tasks = []; again, which would reset your whole list. To update lists or whatever state you else need, you are required to use useState or similar hooks.
Because you also pop your items array, you might want to use two useState hooks and update them instead. Those will handle the internal state for you and isn't lost on a rerender. See https://reactjs.org/docs/hooks-state.html for more information. A solution might look like the following:
import "./styles.css";
import {useState} from 'react'
const BsPlusLg = () => <span >+</span>
const App = () => {
const [items, setItems] = useState( ["Pizza", "Burger", "Shawarma", "Biryani", "Butter Naan", "Panner", "Chapathi"]);
const [tasks, setTasks] = useState([]);
const [searchValue, setSearchValue] = useState("");
const changeValue = (event) => {
setSearchValue(event.target.value);
}
// searchedItems will be updated on each rerender
const searchedItems = tasks.filter((item) => {
if(item.toLowerCase().includes(searchValue.toLowerCase())){
return item;
}
})
const handleClick = () => {
if(items.length === 0){
// keep it simple and stop execution
return;
}
// enforce a new reference
let newItems = [...items];
let item = newItems.pop();
// enforce a new tasks reference
let newTasks = [...tasks, item];
setItems(newItems);
setTasks(newTasks);
}
return (
<div className="App">
<div className='navbar'>
<input type="text" value={ searchValue } onChange={ changeValue } placeholder="Search"></input>
{/* <div className="verticalLine"></div> */}
<button onClick={ handleClick }><BsPlusLg /></button>
</div>
<hr/>
<div className='list'>
<ul>
{(searchValue.length>0)&&(searchedItems.map((item) => {
return <li key={item}>{item}</li>}))
}
{(searchValue.length===0)&&(tasks.map((item) => {
return <li key={item}>{item}</li>
}))
}
</ul>
</div>
</div>
);
}
export default App;
Sandbox: https://codesandbox.io/s/confident-albattani-jf1z6f?file=/src/App.js:0-1655
It means that when React component be re-render, variables is not changed.
you must define 'item' variable as state.
const [tasks, setTasks] = useState([]);
For push it:
setTasks([...tasks, item.pop()])
I am trying to have a list of table fields which can be added and removed at any time.
Issue
As soon as I click delete any element in the middle(except the last one), it deletes all items underneath it
Code is here
I am not sure what is it that's doing wrong, it removes all the elements, even if I try to delete a specific one.
Adding code here also
import React, { useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
const ListComponent = ({ remove, id }) => {
return (
<tr>
<td>
<input />
</td>
<td><button onClick={() => remove(id)}>Delete</button></td>
</tr>
)
}
const App = () => {
const [array, setArray] = useState([]);
const remove = (itemId) => {
setArray(array.filter(each => each.id !== itemId))
}
const addListComponent = () => {
let id = uuidv4();
setArray(oldArray => [...oldArray, {
id,
jsx: <ListComponent key={id} id={id} remove={remove}/>
}])
}
return (
<div>
<button onClick={addListComponent}>Add ListComponent</button>
{array.length > 0 && array.map(each => each.jsx)}
</div>
)
}
export default App;
the issue is everytime you create a new element a new remove function is created with reference to that current state in time. When you create an element, you create a jsx with a remove pointing to that array reference specific in time.
You could fix that changing your remove function to something like:
const remove = (itemId) => {
setArray(prevArr => prevArr.filter(each => each.id !== itemId))
}
which would ensure that you will always have the correct reference to the current state.
Although that solves the issue, I would suggest to follow the guidelines and only store the necessary information at your state, and pass it to JSX at render when you map:
const App = () => {
const [array, setArray] = useState([]);
const remove = (itemId) => {
setArray(array.filter(each => each.id !== itemId))
}
const addListComponent = () => {
let id = uuidv4();
setArray(oldArray => [...oldArray, { id }])
}
return (
<div>
<button onClick={addListComponent}>Add ListComponent</button>
{array.length > 0 && array.map(({ id }) => <ListComponent key={id} id={id} remove={remove}/>)}
</div>
)
}
I am trying to get a to do list item to disappear when I click on it. Everything works except my deleteHandler method, what logic do I need to filter out the item clicked?
import React, { useState } from 'react';
const ToDoList = () => {
const [list, setList] = useState([])
const [item, setItem] = useState("")
const submitHandler = (e) => {
e.preventDefault();
let newList = list.concat(item)
setList(newList);
console.log(item)
e.target.reset();
}
const deleteHandler = (index) => {
console.log(list[index])
// console.log(list)
const newList = list.filter((item) => list.indexOf(item) !== item[index])
console.log(newList)
setList(newList)
}
return(
<div>
<h1>To Do List:</h1>
<form onSubmit={submitHandler}>
<input type='text' onChange={(e)=>setItem(e.target.value)}></input>
<input type='submit' value='Add'></input>
</form>
{list.map((listItem, index) => (
<div key={index} >
<p className="toDoItem" onClick={()=>deleteHandler(index)}>{listItem}</p>
</div>
))}
</div>
)
}
export default ToDoList;
You can solve this by filtering out all the items that don't have that index:
const newList = list.filter((i, itemIndex) => index !== itemIndex)
Note that in a more robust situation, you might want to assign each to do item an ID. Then, you could filter based on that idea. But, for this basic scenario, doing the filter based on index works fine.
Here is the logic that you should try
const deleteHandler = (index) => {
// Assuming that the index means the index of an item which is to be deleted.
const newList = list.filter((item) => list.indexOf(item) !== index);
setList(newList);
}
Change your deleteHandler to something like below:
const deleteHandler = (index) => {
setList(list.filter((i,Id)=> Id !== index))
}