Problem rendering data from Local Storage in React App - javascript

I have problem with rendering data from local storage on every refresh or reload. It renders only hard coded data but not data that I save in LS. It shows data in LS but not rendering. If anyone could explain or tell me what is wrong or give me directions to do something better would be grateful.I am farely new in Reactand would apretiate for insights. I ve put some dummy data. I ve sent componnets which could affect.
import { useState, useEffect } from "react";
//COMPONENTS:
import ScrollToTop from "./components/ScrollToTop";
import Footer from "./components/Footer";
import Home from "./components/Home";
import NavBar from "./components/NavBar";
import PhoneBook from "./components/PhoneBook";
function App() {
const date = new Date().toLocaleDateString();
const [contacts, setContacts] = useState([
{
id: Math.random().toString(36).substr(2, 9),
fullName: "Vekjko Petrovic",
address: "121 Town Commons Way Phoenix, AZ, 45215",
phone: 123_465_689,
date,
},
{
id: Math.random().toString(36).substr(2, 9),
fullName: "Marko Petrovic",
address: "Srbina 35, 11300 Smederevo Srbija",
phone: 256_269_866,
date,
},
{
id: Math.random().toString(36).substr(2, 9),
fullName: "Michael Jackson",
address: "52 City St, Detroit, Mi, 46218",
phone: 359_525_555,
date,
},
{
id: Math.random().toString(36).substr(2, 9),
fullName: "Vanessa Parady",
address: "11 Beogradska Beograd, SRB, 11000",
phone: 123_465_689,
date,
},
]);
useEffect(() => {
const savedContacts = JSON.parse(localStorage.getItem("contacts"));
if (savedContacts) {
setContacts(savedContacts);
}
}, []);
useEffect(() => {
localStorage.setItem("contacts", JSON.stringify(contacts));
}, [contacts]);
const [searchContact, setSearchContact] = useState("");
const [theme, setTheme] = useState("dark");
const changeTheme = () => {
theme === "dark" ? setTheme("light") : setTheme("dark");
};
const addContact = (fullName, phone, address, email) => {
const newContacts = {
id: Math.random().toString(36).substr(2, 9),
fullName,
address,
phone,
email,
date,
};
const newContact = [...contacts, newContacts];
setContacts(newContact);
};
const deleteContact = (id) => {
const remainedContacts = contacts.filter((item) => item.id !== id);
setContacts(remainedContacts);
};
return (
<div data-theme={theme} className="app-container">
<ScrollToTop />
<NavBar changeTheme={changeTheme} currentTheme={theme} />
<Home />
<PhoneBook
contacts={contacts.filter((contact) =>
contact.fullName.toLowerCase().includes(searchContact)
)}
handleAddContact={addContact}
deleteContact={deleteContact}
handleSearchContacts={setSearchContact}
/>
<Footer />
</div>
);
}
export default App;
import React from "react";
import "../index.css";
//ASSETS:
import NewContact from "./NewContact";
import Contact from "./Contact";
import Search from "./Search";
function PhoneBook({
contacts,
handleAddContact,
deleteContact,
handleSearchContacts,
}) {
return (
<div id="phone_book" className="contacts-list">
<Search handleSearchContacts={handleSearchContacts} />
{contacts.map((contact) => {
return (
<Contact
key={contact.id}
id={contact.id}
fullName={contact.fullName}
address={contact.address}
phone={contact.phone}
email={contact.email}
date={contact.date}
deleteContact={deleteContact}
/>
);
})}
<NewContact handleAddContact={handleAddContact} />
</div>
);
}
export default PhoneBook;
import React from "react";
import profile from "../assets/images/profile.png";
import { MdDeleteForever } from "react-icons/md";
function Contact({ fullName, address, phone, email, id, date, deleteContact }) {
return (
<div className="contact">
<p className="contact-header">
<span>
<i>{fullName} </i>
</span>
<img src={profile} alt="profile" />
</p>
<div className="contact-footer">
<p>
{" "}
<i>Address: </i>
{address}
</p>
<p>
<i>Phone:</i> {phone}
</p>
<p>
{" "}
<i>Email:</i> {email}
</p>
<MdDeleteForever
onClick={() => deleteContact(id)}
className="delete-icon"
size="1.3rem"
/>
<p className="span-date">
<i>Date: </i>
{date}
</p>
</div>
</div>
);
}
export default Contact;
import React, { useState } from "react";
function NewContact({ handleAddContact }) {
const [fullName, setFullName] = useState("");
const [phone, setPhone] = useState("");
const [address, setAddress] = useState("");
const [email, setEmail] = useState("");
const handleSaveClick = () => {
if (!(fullName.trim().length > 0)) {
return;
}
handleAddContact(fullName, phone, address, email);
setFullName("");
setPhone("");
setAddress("");
setEmail("");
};
return (
<div className="contact new last">
{" "}
<p className="inputs">
<span>Create New Contact</span>
<label>Full Name:</label>
<input
type="text"
placeholder="Enter..."
value={fullName}
onChange={(e) => setFullName(e.target.value)}
/>
<label> Address:</label>
<input
type="text"
placeholder="Enter..."
value={address}
onChange={(e) => setAddress(e.target.value)}
/>
<label> Phone:</label>
<input
type="text"
placeholder="Enter..."
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
<label>Email:</label>
<input
type="text"
placeholder="Enter..."
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</p>
{/* <img src={profile} alt="fullname" /> */}
<div className="save-list-footer">
<button className="save" onClick={handleSaveClick}>
SAVE
</button>
</div>
</div>
);
}
export default NewContact;
import styled from "styled-components";
import React from "react";
import { MdSearch } from "react-icons/md";
//STYLES
import "../index.css";
function Search({ handleSearchContacts }) {
return (
<SearchComponent className="search">
<MdSearch className="search-icon" size="1.3em" />
<input
type="text"
placeholder="Search..."
onChange={(e) => handleSearchContacts(e.target.value)}
/>
</SearchComponent>
);
}
export default Search;

Took me some messing around but I think I have an example that might be doing what you are describing. It seems like the following code may be the culprit:
useEffect(() => {
localStorage.setItem("contacts", JSON.stringify(contacts));
}, [contacts]);
Not in your example I assume you are initializing contacts like such:
const [contact, setContacts] = useState([])
When this state is initialized it will trigger that useEffect and set the localStorage.setItem("contacts" , []) which will make it look like nothing is being rendered. I think the easiest fix would be to move the localStorage.setItem into the addContacts function.
Here is a simplified version of how to set it up:
export const Contacts = () => {
const [contacts, setContacts] = useState([]);
useEffect(() => {
const savedContacts = localStorage.getItem("contacts");
if (savedContacts) {
setContacts(JSON.parse(savedContacts));
}
}, []);
// useEffect(() => {
// //This is your issue here Comment out this block and comment in the setItem in the addContact
// localStorage.setItem("contacts", JSON.stringify(contacts));
// }, [contacts]);
const addContact = (newContact) => {
const newContactList = [...contacts, newContact];
setContacts(newContactList);
localStorage.setItem("contacts", JSON.stringify(newContactList));
};
return (
<div>
<InputContact addContact={addContact} />
{contacts.map((data, i) => (
<Contact data={data} key={i} />
))}
</div>
);
};
You can find a working example of this on code sandbox. There is some explanation of the app in the App.js and Contacts.jsx. https://codesandbox.io/s/localstorage-contacts-s9dfzc?file=/src/Contacts.jsx:130-969

Related

React - Elements not rendering properly

I'm making a note taking app. I have an array set to state that holds the notes and each note is set to state as an object. My NoteList component maps over the array to render a new Note component when the save button is pressed. Everything works so far, except for the first note. When the first note is saved, the delete button renders but not the user input or date. On every subsequent save, everything renders how it should. I've looked over my code but I'm not sure what is causing this. Can someone please point me in the right direction?
import { useState } from 'react';
import uniqid from 'uniqid';
import NoteList from './components/NoteList';
function App() {
const [note, setNote] = useState({
note: '',
date: '',
id: ''
})
const [notes, setNotes] = useState([])
const [error, setError] = useState(false);
function handleAddNote(text) {
const date = new Date();
const newNote = {
text: text,
date: date.toLocaleDateString(),
id: uniqid()
}
setNote(newNote);
const newNotes = [
...notes,
note
]
setNotes(newNotes);
}
return (
<div className="App">
<h1>My Notes</h1>
<input placeholder='Type to search...'></input>
<NoteList notes={notes} handleAddNote={handleAddNote}/>
</div>
);
}
export default App;
import Note from './Note'
import AddNote from './AddNote'
function NoteList({ notes, handleAddNote }) {
return (
<div className='list-container'>
{notes.map((note) => (
<Note text={note.text} id={note.id} date={note.date}
key={note.id} notes={notes} note={note}/>
))}
<AddNote handleAddNote={handleAddNote}/>
</div>
)
}
export default NoteList;
function Note({ note }) {
return (
<div className='note-container'>
<span className='note-text'>{note.text}</span>
<div className='note-footer'>
<p className='note-date'>{note.date}</p>
<button>Delete</button>
</div>
</div>
)
}
export default Note;
import { useState } from 'react';
function AddNote({ handleAddNote } ) {
const [noteText, setNoteText] = useState('');
function handleChange(e) {
setNoteText(e.target.value);
}
function handleSaveNote() {
if (noteText) {
handleAddNote(noteText);
setNoteText('');
}
}
return (
<div className='new-note-container'>
<textarea onChange={handleChange} value={noteText}
rows='5' cols='30' placeholder='Type to enter a note...'
</textarea>
<div className='count-container'>
<p>Character Count</p>
<button onClick={handleSaveNote}>Save</button>
</div>
</div>
)
}
export default AddNote;
I think that the thing you are missing is that after calling setNote it does not change note on the current render. Only in the next render for that component note will get the new state.
In your case I don't see way you need to have a state for the new note so you can change your App component to be something like this:
function App() {
const [notes, setNotes] = useState([])
const [error, setError] = useState(false);
function handleAddNote(text) {
const date = new Date();
const newNote = {
text: text,
date: date.toLocaleDateString(),
id: uniqid()
}
setNotes((prevNotes) => [...prevNotes, newNote]);
}
return (
<div className="App">
<h1>My Notes</h1>
<input placeholder='Type to search...'></input>
<NoteList notes={notes} handleAddNote={handleAddNote}/>
</div>
);
}
All of these functions, such as adding notes, deleting notes, and searching for notes, are implemented in this code and work properly. I think this might be helpful for you!
import { useState } from "react";
import { uuid } from "uuidv4";
const SelectChip = () => {
const [notes, setNotes] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
function handleAddNote(text) {
const date = new Date();
setNotes((prev) => [
...prev,
{
text: text,
date: date.toLocaleDateString(),
id: uuid()
}
]);
}
return (
<div className="App">
<h1>My Notes</h1>
<input
value={searchTerm}
onChange={(event) => setSearchTerm(event.target.value)}
placeholder="Type to search..."
/>
<NoteList
notes={notes}
setNotes={setNotes}
handleAddNote={handleAddNote}
search={searchTerm}
/>
</div>
);
};
export default SelectChip;
function NoteList({ notes, setNotes, handleAddNote, search }) {
const filteredItems = notes.filter((item) =>
item.text.toLowerCase().includes(search.toLowerCase())
);
return (
<div className="list-container">
{filteredItems.map((note) => {
return (
<Note
text={note.text}
id={note.id}
date={note.date}
key={note.id}
setNotes={setNotes}
note={note}
/>
);
})}
<AddNote handleAddNote={handleAddNote} />
</div>
);
}
function Note({ note, setNotes }) {
function handleDelete(noteId) {
setNotes((prev) => prev.filter((note) => note.id !== noteId));
}
return (
<div className="note-container">
<span className="note-text">{note.text}</span>
<div className="note-footer">
<p className="note-date">{note.date}</p>
<button onClick={() => handleDelete(note.id)}>Delete</button>
</div>
</div>
);
}
function AddNote({ handleAddNote }) {
const [noteText, setNoteText] = useState("");
function handleChange(e) {
setNoteText(e.target.value);
}
function handleSaveNote() {
if (noteText) {
handleAddNote(noteText);
setNoteText("");
}
}
return (
<div className="new-note-container">
<textarea
onChange={handleChange}
value={noteText}
rows="5"
cols="30"
placeholder="Type to enter a note..."
></textarea>
<div className="count-container">
<p>Character Count</p>
<button onClick={handleSaveNote}>Save</button>
</div>
</div>
);
}

How can I get my Note element to update when I change its text?

sorry for the long post, but I'm really stuck in this part of my project. I'm using React to build a notes app, and I can't get my note to update when I alter the text inside of my modal popup...the commented out parts are where I'm having trouble (I wrote what I thought of doing, but don't know how. I've been using React for less than a week). Thank you in advance!
App.js
import React, { useState } from "react";
import { nanoid } from "nanoid";
import NotesList from "./Components/NotesList";
import AddNote from "./Components/AddNote";
const App = () => {
const [notes, setNotes] = useState([
/*empty array of notes*/
]);
const addNote = (title, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: title,
text: text,
date: date.toDateString(),
};
const newNotes = [...notes, newNote];
setNotes(newNotes);
};
const deleteNote = (id) => {
const newNotes = notes.filter((note) => note.id !== id);
setNotes(newNotes);
};
const editNote = (text, title, id) => {
const editDate = new Date();
const editedNote = {
id: nanoid(),
title: title,
text: text,
date: editDate.toDateString(),
};
const editedNotes = notes.map((note) => {
if (note.id == id) {
return editedNote;
}
return note;
});
setNotes(editedNotes);
};
return (
<div className="container">
<AddNote handleAddNote={addNote} />
<NotesList
notes={notes}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
editNote={editNote}
updateNote={updateNote}
/>
</div>
);
};
export default App;
NotesList.js
import Note from "./Note";
import React, { useState } from "react";
import MyModal from "./Modal";
const NotesList = ({ notes, handleDeleteNote, updateNote })=> {
const [open, setOpen] = useState(false);
const [note, setNote] = useState({});
const handleOpen = (id) => {
setNote(notes.filter((note) => note.id === id)[0]);
setOpen(true);
};
const handleClose = () => {
setNote({});
setOpen(false);
};
const clickedTodo = useState({});
return (
<div className="notes-list">
<div className="blank-note"></div>
<div className="existing-notes">
<MyModal
note={note}
clickedTodo={clickedTodo}
handleClose={handleClose}
open={open}
updateNote={updateNote}
/>
{notes.map((note) => (
<Note
id={note.id}
title={note.title}
text={note.text}
date={note.date}
handleOpen={handleOpen}
handleDeleteNote={handleDeleteNote}
/>
))}
</div>
</div>
);
};
export default NotesList;
AddNote.js
import { useState } from "react";
import React from "react";
const AddNote = ({ handleAddNote, isUpdate, note, updateNote }) => {
const [noteTitle, setNoteTitle] = useState("");
const [noteText, setNoteText] = useState("");
const characterLimit = 300;
const handleChange = (event) => {
if (characterLimit - event.target.value.length >= 0) {
setNoteText(event.target.value);
}
};
const handleChange2 = (event) => {
if (characterLimit - event.target.value.length >= 0) {
setNoteTitle(event.target.value);
}
};
const handleSaveClick = () => {
// if (!isUpdate) then do this
// if (!isUpdate) {
(noteText.trim().length > 0)
handleAddNote(noteTitle, noteText);
setNoteText("");
setNoteTitle("");
// } else updateNote(note.id)
// else we use all of the info we have gotten
// we call update note with note.id
};
return (
<div className="note new">
<h2>Add note</h2>
<textarea className="new-text-title"
placeholder={!isUpdate?"Add a title...":note.title}
value={noteTitle}
onChange={handleChange2}
></textarea>
<textarea className="new-text-body"
cols="8"
rows="10"
placeholder={!isUpdate?"Type your note here...":note.text}
value={noteText}
onChange={handleChange}
></textarea>
<div className="note-footer">
<small>Characters remaining: {characterLimit - noteText.length}</small>
<button className="save" onClick={handleSaveClick}>
<strong>Save</strong>
</button>
</div>
</div>
);
};
export default AddNote;
Note.js
import { MdDeleteForever } from "react-icons/md";
const Note = (props) => {
const { id, title, text, date, handleOpen, handleDeleteNote } = props;
const handleOpenModal = (id) => {
handleOpen(id);
};
return (
<div className="note">
<div className="note-upper" onClick={() => handleOpenModal(id)}>
<p className="title">
<textarea className="text-title">{title}</textarea>
</p>
<textarea className="text-body">{text}</textarea>
</div>
<div className="note-footer">
<div className="footer-left" onClick={() => handleOpenModal(id)}>
<small>{date}</small>
</div>
<div className="footer-right">
<MdDeleteForever
onClick={() => {
if (window.confirm("Delete this note?")) {
handleDeleteNote(id);
}
}}
className="delete-icon"
size="1.3em"
/>
</div>
</div>
</div>
);
};
export default Note;
Modal.js
import React, { useState } from "react";
import { Modal, Box } from "#mui/material";
import Note from "./Note";
export default function MyModal(props) {
const { open, handleClose, note, updateNote } = props;
return (
<div onClose={console.log("closing")}>
<Modal open={open} onClose={handleClose}>
<Box
sx={{
position: "relative",
top: "50%",
left: "32%",
outline: "none",
}}
>
<Note
updateNote={updateNote}
note={note}
id={note.id}
title={note.title}
text={note.text}
date={note.date}
/>
</Box>
</Modal>
</div>
);
}
Your Modal.js requires updateNote prop, but in App.js you don't supply it to NotesList.js, thus the parent prop of MyModal.js has no such prop:
<NotesList
notes={notes}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
editNote={editNote}
/>
And later in NotesList.js:
<MyModal
note={note}
clickedTodo={clickedTodo}
handleClose={handleClose}
open={open}
updateNote={updateNote} // this is missing because you are not
// passing this prop from the App component
/>

I have rendered some dummy data from an array but when i try to update it and then render it dynamically, it goes wrong. I have the code here below

I have started learning React.js and i have covered basic concepts like React States, working and events.
just encountered a problem in rendering my data on the screen dynamically
here is the code.
Description: I am trying to create a very small SPA in which the user can enter the name of expense(title), the amount spent(amount), and the date of expense(date).
the user can then add these expenses using the add expense button and it should then be updated as a new expense component in the SPA.
problem: The dummy data(static data from an array) is rendered on the screen.
when I try to add a new expense, this new expense is displayed with the title from the DUMMY_DATA array.
what I want: whenever a user enters a new expense, then this new expense should be added with the entered data, not from DUMMY_DATA.
----APP.JS-----
import React, { useState } from "react";
import Expenses from "./components/Expenses/Expenses";
import NewExpense from "./components/NewExpense/NewExpense";
const DUMMY_EXPENSES = [
{
id: "e1",
title: "Toilet Paper",
amount: 94.12,
date: new Date(2020, 7, 14),
},
{ id: "e2", title: "New TV", amount: 799.49, date: new Date(2021, 2, 12) },
{
id: "e3",
title: "Car Insurance",
amount: 294.67,
date: new Date(2021, 2, 28),
},
{
id: "e4",
title: "New Desk (Wooden)",
amount: 450,
date: new Date(2021, 5, 12),
},
];
const App = () => {
const [expenses, setExpenses] = useState(DUMMY_EXPENSES);
const addExpenseHandler = (expense) => {
setExpenses((previousExpenses) => {
return [expense, ...previousExpenses];
});
};
return (
<div>
<NewExpense onAddExpense={addExpenseHandler} />
<Expenses items={expenses} />
</div>
);
};
export default App;
-----NEW EXPENSE------
import React from "react";
import "./NewExpense.css";
import ExpenseForm from "./ExpenseForm";
const NewExpense = (props) => {
const saveExpenseDataHandler = (enteredExpenseData) => {
const expenseData = { ...enteredExpenseData, id: Math.random().toString() };
console.log("------new expense-------");
console.log(expenseData);
props.onAddExpense(expenseData);
};
return (
<div className="new-expense">
<ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
</div>
);
};
export default NewExpense;
------EXPENSES------
import "./Expenses.css";
import ExpenseItem from "./ExpenseItem";
import Card from "../UI/Card";
import ExpensesFilter from "./ExpensesFilter";
const Expenses = (props) => {
const filterChangeHandler = (selectedYear) => {
console.log("in expenses.js");
console.log(selectedYear);
};
return (
<div>
<Card className="expenses">
<ExpensesFilter onChangeFilter={filterChangeHandler} />
{props.items.map((expense) => (
<ExpenseItem
title={expense.title}
amount={expense.amount}
date={expense.date}
/>
))}
</Card>
</div>
);
};
export default Expenses;
------EXPENSE FORM------
import "./ExpenseForm.css";
import React, { useState } from "react";
const ExpenseForm = (props) => {
const [enteredTitle, setEnteredTitle] = useState("");
const [enteredAmount, setEnteredAmount] = useState("");
const [enteredDate, setEnteredDate] = useState("");
const titleChangeHandler = (event) => {
setEnteredTitle(event.target.value);
// console.log(event.target.value);
};
const amoundChangeHandeler = (event) => {
setEnteredAmount(event.target.value);
};
const dateChangeHandeler = (event) => {
setEnteredDate(event.target.value);
};
const submitHandler = (event) => {
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate),
};
console.log("--------expense form---------");
console.log(expenseData);
props.onSaveExpenseData(expenseData);
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
};
return (
<form onSubmit={submitHandler}>
<div className="new-expense__controls"></div>
<div className="new-expense__control "></div>
<label className="new-expense__control label">Title</label>
<input type="text" value={enteredTitle} onChange={titleChangeHandler} />
<div className="new-expense__controls"></div>
<div className="new-expense__control"></div>
<label>Amount</label>
<input
type="number"
value={enteredAmount}
min="0.01"
step="0.01"
onChange={amoundChangeHandeler}
/>
<div className="new-expense__controls"></div>
<div className="new-expense__control"></div>
<label>Date</label>
<input
type="date"
value={enteredDate}
min="2019-01-01"
max="2022-12-31"
onChange={dateChangeHandeler}
/>
<div className="new-expense__actions">
<button type="submit">Add Expense</button>
</div>
</form>
);
};
export default ExpenseForm;
------EXPENSE ITEM------
import userEvent from "#testing-library/user-event";
import React, { useState } from "react";
import "./ExpenseItem.css";
import ExpenseDate from "./ExpenseDate";
import Card from "../UI/Card";
const ExpenseItem = (props) => {
const [title, setTitle] = useState(props.title);
const clickHandeler = () => {
setTitle("Updated!");
};
return (
<Card className="expense-item">
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{title}</h2>
</div>
<div className="expense-item__price">${props.amount}</div>
<button onClick={clickHandeler}>Change Title</button>
</Card>
);
};
export default ExpenseItem;
here are some snapshots.
1-> initial state
this is the initial state after saving the code and opening it in the browser
2-> here is the data i am going to add
3->error state. i want a a new expense to be formed(abcd) and to show the title amount and date with it
------EXPENSES------
syntax error in rendering title in block
it is props.title instead of just title
import userEvent from "#testing-library/user-event";
import React, { useState } from "react";
import "./ExpenseItem.css";
import ExpenseDate from "./ExpenseDate";
import Card from "../UI/Card";
const ExpenseItem = (props) => {
const [title, setTitle] = useState(props.title);
const clickHandeler = () => {
setTitle("Updated!");
};
return (
<Card className="expense-item">
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
</div>
<div className="expense-item__price">${props.amount}</div>
<button onClick={clickHandeler}>Change Title</button>
</Card>
);
};
export default ExpenseItem;

getting empty item and same item when fetching data react hook

I ma creating app to get pokemons, first I save them in a list and after that show them, but I am getting empty card and after that the same pokemn is showing even if I am searching for another one.
Also I am getting distortion view when I use my component
import { useState, useEffect } from "react";
import "./App.css";
import { v4 } from "uuid";
import Button from "./Components/Button";
import Input from "./Components/Input";
import Label from "./Components/Label";
import Card from "./Components/Card";
import axios from "axios";
function App() {
// const [textFromInput, setTextFromInput] = useState("");
const [name, setName] = useState("");
const [nameFromButtonClick, setNameFromButtonClick] = useState("");
const [pokemon, setPokemon] = useState({
name: "",
picture: "",
id: 0,
type1: "",
type2: "",
});
const [list, setList] = useState([]);
const handleChange = (event) => setName(event.target.value);
const handleClick = () => {
setNameFromButtonClick(name);
setList([...list, pokemon]);
};
// const handleClick = () => setList([...list, pokemon]);
useEffect(() => {
axios
.get(`https://pokeapi.co/api/v2/pokemon/${name}/`)
.then((res) => {
console.log(res);
// setPokemon(res.data);
console.log("res.data=>", res.data);
setPokemon({
name: res.data.name,
picture: res.data.sprites.front_default,
id: res.data.id,
type1: res.data.types[0].type.name,
type2: res.data.types[1].type.name,
});
})
.catch((err) => {
console.log(err);
});
}, [nameFromButtonClick]);
return (
<div className="App">
<div>
<h1>Pokémon Effect</h1>
</div>
<div className="centered">
<div className="container">
{list.map((entry) => (
<Card key ={v4()}
name={entry.name}
picture={entry.picture}
id={entry.id}
type1={entry.type1}
type1={entry.type2}
/>
))}
</div>
<div className="dashboard">
<Input className="input" value={name} onChange={handleChange} />
<Button
className="getPokemon"
text="GetPokemon"
onClick={handleClick}
/>
<Label text={name} />
</div>
</div>
</div>
);
}
export default App;
this is my component Card, I don't know how to make it look like when I ma writing directly in app.js
export default function Card(props){
const{name, picture,id,type1,type2}=props
return(
<div className="card">
<div><img src={picture} alt={name} /></div>
<p>n:{id}</p>
<div> name={name}</div>
<div className="pokeType">
<div className="classType">type={type1}</div>
<div className="classType">type={type2}</div>
</div>
</div>
)
}

Managing the state and working with inputs

I'm making a recipe box App, and I decided to start by my handleTitle and handleInput methods. I was able to pull that out, but now I want to make an array of objects (the objects contain the title and the description) and then I would map through this array and display the data.
but my handleSubmit function is not working the way I wanted. I want the user to be able to write several titles and descriptions, and those will keep being added to the recipes array in the state. Take a look at the code:
import React, { useState } from "react";
import "./styles.css";
import { Form } from "./components/containers/Form";
export default function App() {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [recipe, setRecipe] = useState([]);
const handleTitle = e => {
setTitle(e.target.value);
};
const handleDescription = e => {
setDescription(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
if (title !== "" && description !== "") {
setRecipe(prevState => {
const data = { title: title, description: description };
return {
...prevState,
recipe: prevState.recipe.concat(data)
};
});
}
};
return (
<div className="App">
<Form
title={title}
description={description}
handleTitle={handleTitle}
handleDescription={handleDescription}
handleSubmit={handleSubmit}
/>
</div>
);
}
import React from "react";
export const Form = ({
handleTitle,
handleDescription,
handleSubmit,
title,
description
}) => {
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleTitle}
placeholder="title"
value={title}
/>
<input
type="text"
onChange={handleDescription}
placeholder="description"
value={description}
/>
<button>Add</button>
</form>
</div>
);
};
When you set the recipes, you're changing the primitive type of recipes state to an object. Instead you should just return a new array with the previous recipes and the new recipe.
I've attached a runnable example below:
const Form = ({
handleTitle,
handleDescription,
handleSubmit,
title,
description
}) => {
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleTitle}
placeholder="title"
value={title}
/>
<input
type="text"
onChange={handleDescription}
placeholder="description"
value={description}
/>
<button>Add</button>
</form>
</div>
);
};
function App() {
const [title, setTitle] = React.useState("");
const [description, setDescription] = React.useState("");
const [recipes, setRecipes] = React.useState([]);
const handleTitle = e => {
setTitle(e.target.value);
};
const handleDescription = e => {
setDescription(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
if (title !== "" && description !== "") {
setRecipes(prevState => {
const data = { title: title, description: description };
return [...prevState, data];
});
setTitle("");
setDescription("");
}
};
return (
<div className="App">
<Form
title={title}
description={description}
handleTitle={handleTitle}
handleDescription={handleDescription}
handleSubmit={handleSubmit}
/>
<h5>Recipes</h5>
{recipes.length === 0
? (
<div>No recipes</div>
)
: (
<ul>
{recipes.map(({ title, description }) => (
<li>
{title} : {description}
</li>
))}
</ul>
)}
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Categories

Resources