i make a to do list and i have 2 problems.
The first one : When i write something on my input and i click on my button (addTask) i need to click 2 times to have the result on my console.log . How i can do to have the result directly whn i click the first time ?
The second one : Nothing is rendered on my .map but all my values are on my array todoList . Did i do something wrong ?
function Task() {
const [task, setTask] = useState("");
const [encours, setEncours] = useState("en cours");
const [todoList, setTodoList] = useState([]);
const switchEnCours = () => {
setEncours("terminé");
};
const deleteTask = () => {
setEncours("supprimée");
};
const handleInput = (e) => {
e.preventDefault();
setTask(e.target.value);
};
const AddTask = (e) => {
setTodoList([...todoList, task]);
console.log(todoList);
};
return (
<div>
<input onChange={handleInput}></input>
<button onClick={AddTask}>Valider</button>
<div className="DivColonne">
<div className="Colonne">
<h1>Tâche à faire</h1>
{todoList !== "" ? (
todoList.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>
{todoList.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>{todoList}</p>
</div>
) : (
<div></div>
)}
</div>
</div>
</div>
);
}
export default Task;
React state updates are asynchronously processed. This means you cannot console log the state right after an update has been enqueued and expect to see the updated value. Use a useEffect hook with dependency on the state you are updating to log state updates.
useEffect(() => console.log(todoList), [todoList]);
You are also not returning JSX when you are mapping the todos.
{todoList.map((insertTask) => {
<div>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>;
})}
Should be
{todoList.map((insertTask) => {
return (
<div>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>
);
})}
or directly returned
{todoList.map((insertTask) => (
<div>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>
))}
Don't forget that when mapping lists and arrays in React to use a React key. If your tasks don't have a unique property then I highly suggest adding a GUID to each todo when it's created.
Example:
{todoList.map((insertTask) => (
<div key={task.id}>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>
))}
your 1st problem is that you show data in "console.log()" and the nature of console.log that it shows the previous value/data(state), it's better that you used "alert()" instead.
todoList is array you need iterate here check https://reactjs.org/docs/lists-and-keys.html
<div>
<h1>Tâches terminées</h1>
{encours === "supprimée" ? (
<div>
{todoList.map(value => <p>{value}</p>}
</div>
) : (
<div></div>
)}
</div>
About console.log, you printing results after you change state, state updating is async, so it won't happen instantly.
Can change to
const AddTask = (e) => {
const newList = [...todoList, task]
console.log(newList);
setTodoList(newList);
};
About second part, this condition is redundant {todoList !== "" ? ( todoList is an Array, if it's empty .map never get executed.
Can you comment out this and check {encours === "terminé" ? ( ?
Also can't print Array in jsx
<div>
// <p>{todoList}</p>
<p>{JSON.stringify(todoList, null, 2)</p> --< try this
</div>
There are bunch of wrong things you have done in your code.
1- Since the process of setting a state is Asynchronous, logging the todoList state right after the setTodoList won't give you the latest version of it.
To log the latest version of your state, you have to register an effect:
React.useEffect(() => console.log(todoList), [todoList])
2- It's better to use setTodoList(todoList => [...todoList, task]).
3- Since todoList is an array, checking todoList !== "" is redundant and it will always pass.
4- You missed to return the DOM Node here:
todoList.map((insertTask) => {
<div>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>;
})
5- Also you missed to use a decent key prop on loops:
todoList.map((insertTask) => {
<div key={insertTask.toString()}>
<p>{insertTask}</p>
<button onClick={switchEnCours}>{encours}</button>
</div>;
})
Check out this article for more information on this matter.
And for your main question "How to empty your state" you have to use a controlled text input instead of uncontrolled one:
<input onChange={handleInput} value={task} />
Then to empty your state, all you have to do is:
const AddTask = (e) => {
setTodoList(state => [...state, task]);
setTask("");
};
Related
On every action I am pushing new array of object in the local storage. After that I am mapping over the localstorage data. Now the problem is the UI is not updating autometicaly on every new data inserted to the array of object. But I can see new data on every manual refresh.
Hare is the data I am inserting into the local storage:
const handleSubmit = (e) => {
e.preventDefault();
const input = e.target.input.value;
chatLog.push({ user: "Me", text: `${input}` });
localStorage.setItem("chatLog", JSON.stringify(chatLog));
}
Here I have mapped the data in anorther component:
const chatLog = JSON.parse(localStorage.getItem("chatLog"));
return (
<div>
{chatLog.map((message) => (
<div
className={`chat ${
message.user === "Me" ? "chat-end" : "chat-start"
}`}
>
<div className="chat-image avatar">
<div className="w-10 rounded-full">
<img
src={message.user === "Me" ? Me : Rakib}
alt={message.user === "Me" ? "user" : "rakib"}
/>
</div>
</div>
<div className="chat-header">{message.user}</div>
<div className="chat-bubble">{message.text}</div>
</div>
))}
</div>
);
Now I want that, Everytime the localStorage get new data. Then the UI will update autometicaly.
When new data is added to the localStorage, you need to use some state management solution like React's state or a third-party library like Redux.
One way to achieve this is to use React's state to store the chatLog data and update it whenever a new message is added to the localStorage. Example:
import { useState, useEffect } from "react";
function ChatLog() {
const [chatLog, setChatLog] = useState(JSON.parse(localStorage.getItem("chatLog")) || []);
useEffect(() => {
const handleStorageChange = (e) => {
if (e.key === "chatLog") {
setChatLog(JSON.parse(e.newValue));
}
};
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}, []);
const handleSubmit = (e) => {
e.preventDefault();
const input = e.target.input.value;
const newMessage = { user: "Me", text: `${input}` };
chatLog.push(newMessage);
localStorage.setItem("chatLog", JSON.stringify(chatLog));
setChatLog([...chatLog, newMessage]);
e.target.reset();
};
return (
<div>
{chatLog.map((message, index) => (
<div
key={index}
className={`chat ${
message.user === "Me" ? "chat-end" : "chat-start"
}`}
>
<div className="chat-image avatar">
<div className="w-10 rounded-full">
<img
src={message.user === "Me" ? Me : Rakib}
alt={message.user === "Me" ? "user" : "rakib"}
/>
</div>
</div>
<div className="chat-header">{message.user}</div>
<div className="chat-bubble">{message.text}</div>
</div>
))}
<form onSubmit={handleSubmit}>
<input type="text" name="input" />
<button type="submit">Send</button>
</form>
</div>
);
}
Hi! i have a problem with my state in React, I have two onMouse functions, the first one is to add an element and the second one is to delete, unfortunately the second one does not delete and the added element 'opacity' is rendered.
let menuItems = ['Tasks', 'Issues', 'Files', 'Raports']
const [item, setItem] = useState(menuItems)
const handleSpace = (id) => {
menuItems.splice(id, 0, 'opacity')
setItem([...item])
}
const restart = () => {
menuItems = ['Tasks', 'Issues', 'Files', 'Raports']
setItem([...item])
}
return (
<>
<div className="dashboard" style={slide.flag ? {left: '-105%'} : {left: '0%'}}>
<div className="dashboard__nav">
<ul className="dashboard__nav-list">
{item.map((item, id) => {
return <li className="dashboard__nav-item" key={id} onMouseOver={() => handleSpace(id)} onMouseLeave={restart}>{item}</li>
})}
</ul>
</div>
<div className="dashboard__array">
{tasks.map((task, id) => {
return (
<div className="dashboard__array-item" key={id}>
<div className="dashboard__array-item-header">
<p className="dashboard__array-item-header-title">{task}</p>
<button className="dashboard__array-item-header-cancel">
<FontAwesomeIcon icon={faCancel} />
</button>
</div>
<div className="dashboard__array-item-main">
<p className="dashboard__array-item-main-description">{descriptionTasks[id]}</p>
<p className="dashboard__array-item-main-button">Show More</p>
</div>
</div>
)
})}
</div>
</div>
</>
)
I already created setItem(menuItems), it removed the element 'opacity, but juz it didn't add it a second time
It seems that the two functions might be over complicating the handling of the item state.
Try handle setItem without changing another variable menuItems, so it can be used as a reset value at anytime.
Example:
const menuItems = ["Tasks", "Issues", "Files", "Raports"];
const [item, setItem] = useState(menuItems);
const handleSpace = (id) =>
setItem((prev) => {
const newItems = [...prev];
newItems.splice(id, 0, "opacity");
return newItems;
});
const restart = () => setItem(menuItems);
Hope this will help.
Hello so i tried to make an website using Goole Books API. I want to get the listPrice from the object, but theres some of the book that doesnt have the listPrice in them. So for the example in object number 1 there is some code called saleability: "NOT_FOR_SALE" meanwhile object number 2 have and saleability: "FOR_SALE". If i tried to map the data, there is a error Uncaught TypeError: i.saleInfo.saleability.listPrice is undefined. How do you make spesific condition for this problem.
This is the code :
const CardBooks = (props) => {
const url = "https://www.googleapis.com/books/v1/volumes?q=:keyes&key=AIzaSyDIwDev4gFHRqCh4SSaO9eLKEeI7oYt6aE&maxResults=27"
const result = "&maxResults=40"
const [bookHome, setBookHome] = useState([]);
const [modalShow, setModalShow] = React.useState(false);
useEffect( () => {
axios
.get(`${url}`)
.then( (res) => {
setBookHome(res?.data?.items)
console.log(res?.data?.items)
})
.catch(console.error);
}, [])
return (
<div>
<Container fluid className='wrapper'>
{bookHome && bookHome.map((i, index) => {
return(
<div className='image-container' key={index}>
<div className="book read">
<div className="cover">
<img src={i.volumeInfo.imageLinks.thumbnail} />
</div>
<div className="info">
<h3 className="title">{i.volumeInfo.title}</h3>
</div>
<Example
thumbnail={i.volumeInfo.imageLinks.thumbnail}
title={i.volumeInfo.title}
description={i.volumeInfo.description}
category={i.volumeInfo.categories}
page={i.volumeInfo.pageCount}
language={i.volumeInfo.language}
publisher={i.volumeInfo.publisher}
published={i.volumeInfo.publishedDate}
link={i.volumeInfo.previewLink}
epub={i.accessInfo.epub.isAvailable}
currency={i.saleInfo.saleability.listPrice.currencyCode}
price={i.saleInfo.saleability.listPrice.amount}
/>
</div>
</div>
)
})}
</Container>
</div>
)
}
export default CardBooks
Basically you just need a null/undefined check, a quick and dirty solution:
currency={i.saleInfo.saleability.listPrice ? i.saleInfo.saleability.listPrice.currencyCode : ''}
It's better to use conditional rendering and/or passing the whole object to the component and handling it inside.
I am trying to figure out how to pass an item thru the state on the item: [] inside the list state. Whenever I tried this code, an error shows up as lists is not iterable whenever I insert or add item to the array
Is there a way to insert data to the array property of the state? And adding more string arrays in that property?
const [lists, setLists] = useState({
item: [],
});
const addList = () => {
const listItem = document.getElementById("listItem");
if (listItem.value !== "") {
setLists([
...lists,
{
item: listItem.value,
},
]); // >>> [INSERT TO THE ARRAY PROPERTY]
listItem.value = "";
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
id="listItem"
name="item"
onKeyPress={(e) => (e.key === "Enter" ? addList() : null)}
/>
<button
type="button"
onClick={() => {
addList();
}}
>
Add
</button>
<ul>
LIST
{lists.item.map((val, index) => {
return (
<li key={index}>
<p>{val}</p>
<button type="button" onClick={() => removeList(index)}>
Remove
</button>
</li>
);
})}
</ul>
<button type="submit">submit</button>
</form>
</div>
);
You seem to be having some confusion regarding your data types. lists is an array of objects of the shape {item: ...}.
The useState call should be useState([]).
You'll need lists.map(({item}, index) => (or lists.map(val and val.item) to get at the ....
You can use e.g. console.log(lists), or a debugger, to see what's really happening.)
You shouldn't use document.getElementById() with React, ever. Instead, make the input controlled (or have a ref to it and read the value if you want uncontrolled, but you likely don't).
The setLists call should be the functional form: setLists(lists => [...lists, {item: listItem.value}]).
All in all, something like
function Component() {
const [newItemText, setNewItemText] = React.useState("");
const [todoList, setTodoList] = React.useState([]);
const addList = (event) => {
event.preventDefault();
if (newItemText !== "") {
setTodoList(todoList => [
...todoList,
{
item: newItemText,
},
]);
setNewItemText("");
}
};
return (
<div>
<form onSubmit={addList}>
<input
type="text"
name="item"
value={newItemText}
onChange={e => setNewItemText(e.target.value)}
/>
<button
type="submit"
>
Add
</button>
</form>
<ul>
LIST
{todoList.map(({item}, index) => {
return (
<li key={index}>
<p>{item}</p>
<button type="button">
Remove
</button>
</li>
);
})}
</ul>
</div>
);
}
ReactDOM.render(<Component />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="root">
const [lists, setLists] = useState({
item: [],
});
In the code above you set initial value as an Object not an Array. Try to change code like below.
const [lists, setLists] = useState([]);
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