I make a very simple todo list and should be next to each generated items a delete-btn.
How can I write this function via OnClick()?
i have in App.js quite normal array and now should be deleted with Delete-btn any added items.
ShowListComponent:
export default function ShowList ({items}){
const isEmpty = "Die Eingabe Feld muss nicht leer sein";
return(
<div className='itemList'>
{items.map((item) => (
<ul className='itemContainer'>
<li className='item-name'>
{item.itemName}
</li>
<div className='delete-btn'>
<Button type="primary" danger ghost>
Delete
</Button>
</div>
</ul>
))}
</div>
);
}
List Component:
export default function List ({items, setItems}){
const [inputValue, setInputValue] = useState('');
const AddButtonClick = () => {
if (inputValue === ""){
alert("Eingabefeld ist Leer");
return false
} else {
const newItem ={
itemName: inputValue,
};
const newItems = [...items,newItem];
setItems(newItems);
setInputValue('');
}
}
return(
<>
<Input value={inputValue} onChange={(event) =>
setInputValue(event.target.value)}
name='input'
className='addItemInput'
placeholder="Artikel hinzufügen"
/>
<Button
type="primary"
className='btn'
onClick={() => AddButtonClick()}
>Hinzufügen</Button>
</>
)
}
like the AddButtonClick() create deleteButtonClick:
const deleteButtonClick = (index) => {
const newItems = items && items.filter((element , i) => i !== index);
setItems(newItems);
}
pass it to ShowList component and use it like so:
export default function ShowList ({items, deleteButtonClick}){
const isEmpty = "Die Eingabe Feld muss nicht leer sein";
return(
<div className='itemList'>
{items.map((item, index) => (
<ul className='itemContainer'>
<li className='item-name'>
{item.itemName}
</li>
<div className='delete-btn'>
<Button type="primary" danger ghost onClick={() => deleteButtonClick(index)}>
Delete
</Button>
</div>
</ul>
))}
</div>
);
}
Related
I want to make a component in the front page that displays list of vehicles with navigation buttons that shows list of the selected button.
Is there a better way I could write this code below because I want the list of categories to be much longer? Or are there other methods I can use other than useState & useEffect? I'm using Next.js
const Vehicles = () => {
const [selectedCategory, setSelectedCategory] = useState("cars")
const [cars, setCars] = useState(true)
const [bikes, setBikes] = useState(false)
const [motorcycles, setMotorcyles] = useState(false)
const [scooters, setScooters] = useState(false)
useEffect(() => {
if (selectedCategory === "cars") {
setCars(true)
setBikes(false)
setMotorcyles(false)
setScooter(false)
}
if (selectedCategory === "bikes") {
setBikes(true)
setCars(false)
setMotorcyles(false)
setScooters(false)
}
if (selectedCategory === "motorcycles") {
setMotorcyles(true)
setCars(false)
setBikes(false)
setScooters(false)
}
if (selectedCategory === "scooters") {
setScooters(true)
setCars(false)
setBikes(false)
setMotorcyles(false)
}
}, [selectedCategory])
return (
<div>
<div>
<button onClick={() => setSelectedCategory("cars")}> Cars </button>
<button onClick={() => setSelectedCategory("bikes")}> Bikes </button>
<button onClick={() => setSelectedCategory("motorcycles")}> Motorcycles </button>
<button onClick={() => setSelectedCategory("scooters")}> Scooters </button>
</div>
{cars && (
<div>
<Cars />
</div>
)}
{bikes && (
<div>
<Bikes />
</div>
)}
{motorcycles && (
<div>
<Motorcycles />
</div>
)}
{scooters && (
<div>
<Scooters />
</div>
)}
</div>
);
};
export default Vehicles;
These things like abstraction and code refactor tend to differ from person to person. But what I would generally prefer is something between abstraction and readability. Since readability is much more important I would go along this way
const allCategory = {
cars: 0,
bikes: 1,
motorcycles: 2,
scooters: 3
}
const Vehicles = () => {
// set cars as default category. If you don't want it change it to be any integer before 0
const [category, setCategory] = useState(allCategory.cars)
return (
<div>
<div>
<button onClick={() => setCategory(allCategory.cars)}> Cars </button>
<button onClick={() => setCategory(allCategory.bikes)}> Bikes </button>
<button onClick={() => setCategory(allCategory.motorcycles)}> Motorcycles </button>
<button onClick={() => setCategory(allCategory.scooters)}> Scooters </button>
</div>
<div>
{category === allCategory.cars && <Cars />}
{category === allCategory.bikes && <Bikes />}
{category === allCategory.motorcycles && <Motorcycles />}
{category === allCategory.scooters && <Scooters />}
</div>
</div>
);
};
export default Vehicles;
If you want to go absolutely crazy. You can decrease this to be in about 4-5 lines in return statement. But I prefer this to be much cleaner
I think you could do something more like
const Vehicles = () => {
const [selectedCategory, setSelectedCategory] = useState("cars")
const renderCat = (category) =>{
switch(category){
case "cars":
return <Cars />
//etc...
}
}
return (
<div>
<div>
<button onClick={() => setSelectedCategory("cars")}> Cars </button>
<button onClick={() => setSelectedCategory("bikes")}> Bikes </button>
<button onClick={() => setSelectedCategory("motorcycles")}> Motorcycles </button>
<button onClick={() => setSelectedCategory("scooters")}> Scooters </button>
</div>
<div>
{renderCat(selectedCategory)}
</div>
</div>
);
};
export default Vehicles;
You could do like this:
const categories = [
{
name: "cars",
title: "Cars",
component: Cars
},
{
name: "bikes",
title: "Bikes",
component: Bikes
},
{
name: "motorcyles",
title: "Motorcyles",
component: Motorcyles
},
{
name: "scooters",
title: "Scooters",
component: Scooters
}
];
const Vehicles = () => {
const [selectedCategory, setSelectedCategory] = useState("cars")
return (
<div>
<div>
{categories.map(({ name, title }) => (
<button onClick={() => setSelectedCategory(name)}>
{title}
</button>
))}
</div>
{categories.map(({ component: Component, name }) => {
if(selectedCategory !== name) return;
return (
<div>
<Component />
</div>
);
})}
</div>
);
};
export default Vehicles;
The category objects can be different depending on what you want to display. Having both name and title is not essential, it's just an example.
In react js i create a todo app but when i am doing todo-delete it by default delete the first item why?In react js i create a todo app but when i am doing todo-delete it by default delete the first item why?In react js i create a todo app but when i am doing todo-delete it by default delete the first item why?
import React, { useState } from "react";
import "./App.css";
const Todo = () => {
const [inputData, setInputData] = useState("");
const [items, setItems] = useState([]);
const addItem = (e) => {
let pattern = /\s/g.test(inputData);
// This pattern check empty space and InputData is checking null
if (pattern === false && inputData !== "") {
setItems([...items, inputData]);
setInputData("");
}
setInputData("");
};
// delete items
const deleteItem = (idx) => {
// console.log(idx);
let temp = [...items];
// console.log(temp);
temp.splice(idx, 1);
setItems(temp);
};
// done items
// const doneItem = (idx) => {};
return (
<>
<div className="container">
<h1 className="bg-dark text-white">Todo App</h1>
<div className="d-flex p-2 justify-content-between">
<input
type="text"
className="form-control"
placeholder="Add Your New Todo"
value={inputData}
onChange={(e) => {
setInputData(e.target.value);
// console.log(e);
// console.log(e.nativeEvent.data);
}}
/>
<button className="btn btn-dark m-1" onClick={addItem}>
+
</button>
</div>
{items.map((element, idx) => {
console.log(element);
console.log(idx);
return (
<div
className="d-flex justify-content-between p-2 bg-dark text-white"
key={idx}
>
<h4>{element}</h4>
<button
className="btn btn-danger"
onClick={(idx) => {
deleteItem(idx);
}}
>
X
</button>
{/* <button onClick={() => doneItem(idx)}>-</button> */}
</div>
);
})}
</div>
</>
);
};
export default Todo;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
The problem starts in this code.
<button
className="btn btn-danger"
onClick={(idx) => { // problem starts here
deleteItem(idx);
}}
>
X
</button>;
You are already using idx as an index for your values in the map function. Button basically gives you the event and you are representing the same variable. Just remove that variable from the button onClick event and everything works fine.
Attached a sandbox
I'm trying to render an array of value . This is basicaly a to do list and i want to render different value with the button "en cours"
My .map doesn't seems to work and my array just record one value. someone can help me ?
There is also an other problem, when i write on my input my letters just disappeared directly ??
function Task() {
const [task, setTask] = useState("");
const [encours, setEncours] = useState("");
let toDo = [];
const handleInputTask = (e) => {
setTask(e.target.value);
setEncours("en cours");
};
function AddTask() {
toDo.push(task);
console.log(toDo);
}
const switchEnCours = () => {
setEncours("terminé");
};
const deleteTask = () => {
setEncours("supprimée");
};
function RenderMesTasks() {
return (
<div>
<input onChange={handleInputTask}></input>
<button onClick={AddTask}>Valider</button>
<div className="DivColonne">
<div className="Colonne">
<h1>Tâche à faire</h1>
{toDo !== "" && encours === "en cours" ? (
toDo.map((insertTask) => {
<div>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>;
})
) : (
<div></div>
)}
</div>
<div className="Colonne">
<h1>Tâche en cours</h1>
{encours === "terminé" ? (
<div>
{toDo.map((insert) => {
return (
<div>
<p>{insert}</p>
<button onClick={deleteTask}>{encours}</button>
</div>
);
})}
</div>
) : (
<div></div>
)}
</div>
<div>
<h1>Tâches terminées</h1>
{encours === "supprimée" ? (
<div>
<p>{toDo}</p>
</div>
) : (
<div></div>
)}
</div>
</div>
</div>
);
}
return (
<div>
<h2> Ecrivez une nouvelle tâche</h2>
<div>
<RenderMesTasks />
</div>
</div>
);
}
export default Task;
todos is state:
const [todos,setTodos] = useState([]);
const addToDo = (todo) => setTodos([...todos,todo])
Add this state variable
const [todoList, setTodoList] = useState([]);
and then in your AddTask(), add this
function AddTask() {
setTodoList([...todoList, task]);
}
I am trying to click on one card of a dynamically created list using map(). I want to click on one card from the array and add a class to it, while at the same time deselecting the other card that was previously clicked. How can I accomplish this? This is what I have so far:
const CardList = () => {
return (
<div className='card-list'>
{CardData.map(({ id, ...otherData }) => (
<Card key={id} {...otherData} />
))}
</div>
);
};
export default CardList;
const Card = ({
headline,
time,
views,
thumbImg,
trainerImg,
workouts,
id
}) => {
const [isSelected, setIsSelected] = useState(false);
const [clickId, setClickId] = useState('');
function handleClick(id) {
setIsSelected(!isSelected);
setClickId(id);
}
return (
<div
className={`card ${isSelected && clickId === id ? 'clicked' : ''}`}
onClick={() => handleClick(id)}
>
<div className='thumbnail-div'>
<img className='thumbnail-img' src={thumbImg} alt='video' />
{workouts ? (
<div className='workout-overlay'>
<p>{workouts}</p>
<p className='workouts'>workouts</p>
</div>
) : null}
</div>
<div className='card-info'>
<div className='card-headline'>
<p>{headline}</p>
<img src={trainerImg} alt='trainer' />
</div>
{time && views ? (
<div className='trainer-data'>
<span>
<i className='glyphicon glyphicon-time'></i>
{time}
</span>
<span>
<i className='glyphicon glyphicon-eye-open'></i>
{views}
</span>
</div>
) : null}
</div>
</div>
);
};
export default Card;
The parent component should control what card is clicked. Add className property to card component:
const Card = ({
//...
className,
onClick
}) => {
//...
return (
<div
className={`card ${className}`}
onClick={() => onClick(id)}
>...</div>
)
}
In parent component pass the className 'clicked' and add the onClick callback to set the selected card:
const CardList = () => {
const [isSelected, setIsSelected] = useState(null);
const handleClick = (id) => {
setIsSelected(id);
}
return (
<div className='card-list'>
{CardData.map(({ id, ...otherData }) => (
<Card key={id} className={isSelected===id && 'clicked'} onClick ={handleClick} {...otherData} />
))}
</div>
);
};
You can do something like this.
First you don't have to set state to each card. Instead Lift state Up.
You define which card is selected in parent so you can pass that to children and add classes if current selected is matching that children.
const CardList = () => {
const [isSelected, setIsSelected] = useState();
const handleCardClick = (id) => {
setIsSelected(id);
}
return (
<div className='card-list'>
{CardData.map(({ id, ...otherData }) => (
<Card key={id} {...otherData} handleClick={handleCardClick} isSelected={isSelected}/>
))}
</div>
);
};
export default CardList;
const Card = ({
headline,
time,
views,
thumbImg,
trainerImg,
workouts,
id,
isSelected,
handleClick
}) => {
return (
<div
className={`card ${isSelected === id ? 'clicked' : ''}`}
onClick={() => handleClick(id)}
>
<div className='thumbnail-div'>
<img className='thumbnail-img' src={thumbImg} alt='video' />
{workouts ? (
<div className='workout-overlay'>
<p>{workouts}</p>
<p className='workouts'>workouts</p>
</div>
) : null}
</div>
<div className='card-info'>
<div className='card-headline'>
<p>{headline}</p>
<img src={trainerImg} alt='trainer' />
</div>
{time && views ? (
<div className='trainer-data'>
<span>
<i className='glyphicon glyphicon-time'></i>
{time}
</span>
<span>
<i className='glyphicon glyphicon-eye-open'></i>
{views}
</span>
</div>
) : null}
</div>
</div>
);
};
export default Card;
In this simple React App, I don't understand why I get the following warning message:
Warning: Each child in a list should have a unique "key" prop.
To me it seems that I put the key at the right place, in form of key={item.login.uuid}
How can I get rid of the warning message?
Where would be the right place to put the key?
App.js
import UserList from './List'
const App = props => {
const [id, newID] = useState(null)
return (
<>
<UserList id={id} setID={newID} />
</>
)
}
export default App
List.js
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const renderItem = (item, newID) => {
return (
<>
{newID ? (
// User view
<div key={item.login.uuid}>
<div>
<h2>
{item.name.first} {item.name.last}
</h2>
<p>
{item.phone}
<br />
{item.email}
</p>
<button onClick={() => setID(null)}>
Back to the list
</button>
</div>
</div>
) : (
// List view
<li key={item.login.uuid}>
<div>
<h2>
{item.name.first} {item.name.last}
</h2>
<button onClick={() => setID(item.login.uuid)}>
Details
</button>
</div>
</li>
)}
</>
)
}
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <div>{renderItem(user, true)}</div>
} else {
// List view
return (
<ul>
{resources.map(user => renderItem(user, false))}
</ul>
)
}
}
export default UserList
The key needs to be on the root-level element within the loop. In your case, that's the fragment (<>).
To be able to do that, you'll need to write it out fully:
const renderItem = (item, newID) => {
return (
<Fragment key={item.login.uuid}>
{newID ? (
...
)}
</Fragment>
);
}
(You can add Fragment to your other imports from react).
Note that the fragment isn't actually needed in your example, you could drop it and keep the keys where they are since then the <div> and <li> would be the root element:
const renderItem = (item, newId) => {
return newID ? (
<div key={item.login.uuid}>
...
</div>
) : (
<li key={item.login.uuid}>
...
</li>
)
}
What if you create 2 separate components, one for the user view and one for the list item. That way you only need to pass the user prop. Also, use JSX and pass wht key from there.
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const User = ({user}) => (
<div key={user.login.uuid}>
<div>
<h2>
{user.name.first} {user.name.last}
</h2>
<p>
{user.phone}
<br />
{user.email}
</p>
<button onClick={() => setID(null)}>
Back to the list
</button>
</div>
</div>
)
const ListItem = ({user}) => (
<li key={user.login.uuid}>
<div>
<h2>
{user.name.first} {user.name.last}
</h2>
<button onClick={() => setID(user.login.uuid)}>
Details
</button>
</div>
</li>
)
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <User user={user}</div>
} else {
// List view
return (
<ul>
{resources.map((user, index) => <ListItem key={index} user={user} />)}
</ul>
)
}
}
export default UserList