I am new to react and I am just playing around with some code to familiarize myself with it.
I have data stored into an array of objects, as if I was bringing it in from a JSON-based API. I am using states so I can remove or add any item to the data.
I have succeeded to remove an element from the array of objects. To add to the list, I am using a form. I have my form setup in Form.js.
Form.js
import { useState } from "react";
function Form() {
const [name, setName] = useState("");
const [job, setJob] = useState("");
const submitForm = (e, {handleSubmit}) => {
e.preventDefault();
const newChar = {
name: e.target.name.value,
job: e.target.job.value
}
handleSubmit(newChar);
setName('');
setJob('');
}
return (
<form onSubmit={submitForm}>
<label htmlFor="name">Name</label>
<input
type="text"
name="name"
id="name"
value={name}
onChange={(e) => setName(e.target.value)} />
<label htmlFor="job">Job</label>
<input
type="text"
name="job"
id="job"
value={job}
onChange={(e) => setJob(e.target.value)} />
<input type="submit"/>
</form>
);
}
export default Form;
In App.js, I have initialized my array of objects and I have my function handleSubmit() that will update the state by taking the existing state and adding the new character parameter, using the ES6 spread operator.
import { useState } from "react";
import Form from "./Form";
function App() {
const [characters, setCharacters] = useState([
{
name: 'Charlie',
job: 'Janitor',
},
{
name: 'Mac',
job: 'Bouncer',
},
{
name: 'Dee',
job: 'Aspring actress',
},
{
name: 'Dennis',
job: 'Bartender',
},
]);
const removeChar = (id) => {
const newChars = characters.filter(character => characters.indexOf(character) !== id);
setCharacters(newChars);
}
const handleSubmit = (character) => {
setCharacters([ ...characters, character ]);
}
return (
<div className="container">
<Table characters = {characters} removeChar = {removeChar} />
<Form handleSubmit = {handleSubmit} />
</div>
);
}
export default App;
This code does not work. I cannot seem to figure out why. any help would be greatly appreciated
just try this
const handleSubmit = (character) => {
let oldArray = [...characters]
oldArray.push(character)
setCharacters(oldArray);
}
Check this Example
characters = [
{
name: 'Charlie',
job: 'Janitor',
},
{
name: 'Mac',
job: 'Bouncer',
},
{
name: 'Dee',
job: 'Aspring actress',
},
{
name: 'Dennis',
job: 'Bartender',
},
]
const handleSubmit = (character) => {
let oldArray = [...characters]
oldArray.push(character)
console.log(oldArray)
//setCharacters([ ...characters, character ]);
}
handleSubmit({name:'Waleed',Job:"HOD"})
So I found where the problem was lying. I was passing the {handleSubmit} in the const SubmitForm declaration instead of passing it to the const Form() .
Here is the code if it can help anyone new to react.
Form.js
import { useState } from "react";
function Form({handleSubmit}) {
const [name, setName] = useState("");
const [job, setJob] = useState("");
const submitForm = (e) => {
e.preventDefault();
const newChar = {
name: e.target.name.value,
job: e.target.job.value
}
handleSubmit(newChar);
setName('');
setJob('');
}
return (
<form onSubmit={submitForm}>
<label htmlFor="name">Name</label>
<input
type="text"
name="name"
id="name"
value={name}
onChange={(e) => setName(e.target.value)} />
<label htmlFor="job">Job</label>
<input
type="text"
name="job"
id="job"
value={job}
onChange={(e) => setJob(e.target.value)} />
<input type="submit"/>
</form>
);
}
export default Form;
App.js
import { useState } from "react";
import Form from "./Form";
function App() {
const [characters, setCharacters] = useState([
{
name: 'Charlie',
job: 'Janitor',
},
{
name: 'Mac',
job: 'Bouncer',
},
{
name: 'Dee',
job: 'Aspring actress',
},
{
name: 'Dennis',
job: 'Bartender',
},
]);
const handleSubmit = (character) => {
setCharacters(characters => [...characters, character])
}
return (
<div className="container">
<Form handleSubmit = {handleSubmit} />
</div>
);
}
export default App;
Related
it updates only the lastly typed input box value in the state and other are undefined
i get this in console
Object { Name: undefined, Age: "123", City: undefined }
second time
Object { Name: undefined, Age: undefined, City: "city" }
Form.jsx
import React, {useState} from 'react';
const Form = (props) => {
const [formData, setFormData] = useState({ Name:'', Age:'', City:''});
const infoChange = e => {
const { name,value} = e.target;
setFormData({
[e.target.name]: e.target.value,
})
}
const infoSubmit = e =>{
e.preventDefault();
let data={
Name:formData.Name,
Age:formData.Age,
City:formData.City
}
props.myData(data);
}
return (
<div className="">
<form onSubmit={infoSubmit} autoComplete="off">
<div className="form-group mb-6">
<label className="">Name:</label>
<input type="text" onChange={infoChange} name="Name" value={formData.Name} className=""placeholder="Enter Name" />
</div>
<div className="form-group mb-6">
<label className="">City:</label>
<input type="text" onChange={infoChange} name="City" value={formData.City} className=""
placeholder="Enter Age" />
</div>
<button type="submit" className="">Submit</button>
</form>
</div>
);
};
export default Form;
App.jsx
this is App.jsx file, here i get the data prop and display it in console.log
import React from 'react';
import Form from './components/Form';
import Table from './components/Table';
const App = () => {
const create = (data) => {
console.log(data);
}
return (
<div className='flex w-full'>
<div className=''>
<Form myData={create} />
</div>
<div className=''>
<Table />
</div>
</div>
);
};
export default App;
You're stomping the previous state with the most recent change. If you want to preserve the existing state you have to include it in the update.
setFormData({
...formData,
[e.target.name]: e.target.value,
})
with react-hooks you need to set the entire object again.
const [formData, setFormData] = useState({ Name:'', Age:'', City:''});
const infoChange = e => {
const { name,value} = e.target;
setFormData({
// spread the current values here
...formData,
// update the current changed input
[name]: value,
})
or, even better IMHO. You have one state for each prop
const [name, setName] = useState('');
const [age, setAge] = useState('');
const [city, setCity] = useState('');
// ...
<input onChange={({target: {value}}) => setName(value)} />
<input onChange={({target: {value}}) => setAge(value)} />
<input onChange={({target: {value}}) => setCity(value)} />
Change this
const infoChange = e => {
const { name,value} = e.target;
setFormData({...formData
[e.target.name]: e.target.value,
})
}
I have a state that contains another array. I need to get this array to return it as a list. The new Item should appear as an object in the application array. I don't quite understand what I did wrong. How can I fix this?
** enter image description here
import React, { useState } from 'react'
function App() {
const [data, setData] = useState([
{
name: 'Ivan Pupkin',
email: 'ivan#gmail.com',
phone: '+34452344323',
application: [
{
nameOfApp: 'Name of App',
type: 'It and business',
description: 'some description',
},
],
},
])
const [name, setName] = useState('');
const [type, setType] = useState('');
const [description, setDescription] = useState('');
const addNewUser = (e) => {
e.preventDefault()
setData(current => current.map(item => [...item.application, {
personalId: 4,
nameOfApp: name,
description: description,
type: type
}]))
}
const Users = data.map(item => item.application.map((elem, index) => {
return(
<div key={index}>
<div>{elem.nameOfApp}</div>
<div>{elem.type}</div>
<div>{elem.description}</div>
</div>
)
}))
return (
<div>
<form action="#">
<input onChange={(e) => setName(e.target.value)} placeholder='name'/>
<input onChange={(e) => setType(e.target.value)} placeholder='type'/>
<input onChange={(e) => setDescription(e.target.value)} placeholder='desc'/>
<button onClick={addNewUser} type='submit'>submit</button>
</form>
<br />
<br />
<br />
{Users}
</div>
)
}
export default App
I'm not sure if I've understand what you're tring to do but here is a working version :
Basically, I just say which user I want to edit in addUser() then search it, assign new application to it, and return the array of users without the previous user
If I were you, I would avoid to have objects / array without ids :)
import React, { useState } from "react";
function App() {
const [data, setData] = useState([
{
id: 1,
name: "Ivan Pupkin",
email: "ivan#gmail.com",
phone: "+34452344323",
application: [
{
id: 1,
nameOfApp: "Name of App",
type: "It and business",
description: "some description"
}
]
}
]);
const [name, setName] = useState("");
const [type, setType] = useState("");
const [description, setDescription] = useState("");
const addNewUser = (e, targetUserId) => {
e.preventDefault();
setData((prevUsers) => {
const editedUser = prevUsers.find(({ id }) => id === targetUserId);
editedUser.application.push({
nameOfApp: name,
type: type,
description: description
});
const newUsers = [
...prevUsers.filter(({ id }) => id !== targetUserId),
editedUser
];
return newUsers;
});
};
const Users = data.map((item) =>
item.application.map((elem, index) => {
return (
<div key={index}>
<div>{elem.nameOfApp}</div>
<div>{elem.type}</div>
<div>{elem.description}</div>
</div>
);
})
);
return (
<div>
<form action="#">
<input onChange={(e) => setName(e.target.value)} placeholder="name" />
<input onChange={(e) => setType(e.target.value)} placeholder="type" />
<input
onChange={(e) => setDescription(e.target.value)}
placeholder="desc"
/>
<button onClick={(e) => addNewUser(e, 1)} type="submit">
submit
</button>
</form>
<br />
<br />
<br />
{Users}
</div>
);
}
export default App;
Hope it helped you !
I am exploring the react onChange feature. The functionality I would like to do is
when checkbox is selected, I would like to update local data to add some value
when checkbox is unselected, I would like to just populate the original data
the code I have seems do the reverse of what I want. any can provide some guideline?
codesandbox
import "./styles.css";
import { useState } from "react";
export default function App() {
const localData = [{ name: "apple", phone: 12345 }];
const [check, setCheck] = useState(false);
const [data, setData] = useState(localData);
const handleOnChange = () => {
setCheck(!check);
check ? setData([...data, { name: "amazon", phone: 222 }]) : setData(data);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<p>
<input type="checkbox" check onChange={handleOnChange}></input>
</p>
<p>{JSON.stringify(data)}</p>
</div>
);
}
The setCheck runs asyncranously. So in the function handleOnChange, right after setCheck(!check), check's value has not been set to the new value yet.
You could use an effect here since you want the side-effect of changing data from changing check. This way, whenever check is done changing, the effect triggers:
const handleOnChange = () => {
setCheck(!check);
};
useEffect(() => {
check &&
setData((previous) => [...previous, { name: "amazon", phone: 222 }]);
}, [check]);
Updated sandbox: https://codesandbox.io/s/quirky-rain-hmhkk
There is no need of using two states, that solution will give you more problems when you want to add more options.
I would go for an approach similar to this one:
import "./styles.css";
import { useState } from "react";
const localData = [{ name: "apple", phone: 12345 }];
const extraData = [{ name: "amazon", phone: 222 }];
export default function App() {
const [data, setData] = useState(localData);
const handleOnChange = (item) => {
if (!data.find(({ name }) => name === item.name)) setData([...data, item]);
else setData(data.filter(({ name }) => name !== item.name));
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<p>
{extraData.map((item) => {
return (
<input
key={item.name}
type="checkbox"
checked={data.includes(({ name }) => name === item.name)}
onChange={() => handleOnChange(item)}
/>
);
})}
</p>
<p>{JSON.stringify(data)}</p>
</div>
);
}
This will allow you to add more checkboxes in the future.
So, you want the data to be added to data when the checkbox IS checked? Just invert the condition of the ternary that you use in handleOnChange:
import "./styles.css";
import { useState } from "react";
export default function App() {
const localData = [{ name: "apple", phone: 12345 }];
const [check, setCheck] = useState(false);
const [data, setData] = useState(localData);
const handleOnChange = () => {
setCheck(!check);
!check ? setData([...data, { name: "amazon", phone: 222 }]) : setData(data);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<p>
<input type="checkbox" check onChange={handleOnChange}></input>
</p>
<p>{JSON.stringify(data)}</p>
</div>
);
}
I am new to React.
I am trying to use Filter component but it is giving me this error: "Invalid hook call. Hooks can only be called inside of the body of a function component."
I think the problem is somewhere in the onChange={handleFilterChange} but I do not know what. Any clues?
Here is my App.js:
import React, { useState } from 'react'
import Filter from './components/Filter'
import Person from './components/Person'
const App = () => {
const [persons, setPersons] = useState([
{ name: 'Arto Hellas', number: '040-123456' },
{ name: 'Ada Lovelace', number: '39-44-5323523' },
{ name: 'Dan Abramov', number: '12-43-234345' },
{ name: 'Mary Poppendieck', number: '39-23-6423122' }
])
const [newName, setNewName] = useState('')
const [newNumber, setNewNumber] = useState('')
const [filter, setFilter] = useState('')
const handleNameChange = (event) => {
console.log("name", event.target.value)
setNewName(event.target.value)
}
const handleNumberChange = (event) => {
console.log("number", event.target.value)
setNewNumber(event.target.value)
}
const handleFilterChange = (event) => {
console.log("filter", event.target.value)
setFilter(event.target.value)
}
const peopleToShow = filter
? persons.filter(person => person.name.toLowerCase().includes(filter.toLowerCase()))
: persons
const handleAddPerson = (event) => {
// prevent default form action
event.preventDefault();
console.log('button clicked', event.target);
console.log("newname", newName)
let checkNameExists = persons.filter(person => (person.name === newName))
let checkNumberExists = persons.filter(person => (person.number === newNumber))
if (checkNameExists.length === 0 && checkNumberExists.length === 0) {
const nameObject = {
name: newName,
number: newNumber
}
// dont mutate state. use set method (make new object)
setPersons(persons.concat(nameObject))
} else if (checkNameExists.length !== 0) {
alert(`${newName} is already added to phonebook`);
} else {
alert(`${newNumber} is already added to phonebook`);
}
// reset name and number
setNewName('')
setNewNumber('')
}
return (
<div>
<h2>Phonebook</h2>
<Filter value={filter} onChange={handleFilterChange}/>
{/* <div>
filter shown with:<input value={filter}
onChange={handleFilterChange} />
</div> */}
<h2>add a new contact</h2>
<form onSubmit={handleAddPerson}>
<div>
name:<input value={newName}
onChange={handleNameChange} />
</div>
<div>number: <input value={newNumber}
onChange={handleNumberChange} /></div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
<div>
{peopleToShow.map(person =>
<Person key={person.name} person={person} />
)}
</div>
</div>
)
}
export default App
Here is my Filter.js:
import React from 'react';
const Filter = (props) => {
return (
<div>
filter shown with:<input value={props.filter}
onChange={props.handleFilterChange} />
</div>
)
}
export default Filter
you can not pass the reference of your setFilter func to anywhere else in the props. That's why you've used the handler function in the first place.. you should rename the prop. its a better practice.
and then again, in your filter js you should use the prop setFilter that you've passed. you haven't passed handleFilterChange func in the props.. you tried to import that func.. but I believe you have not exported it.. and you did not follow the named import convention..
Change
<Filter value={filter} onChange={handleFilterChange}/>
to
<Filter filter={filter} handleFilterChange={handleFilterChange} />
this is my simple book-list application using react context API. I have created a context to addBook, removeBook, and entire Book state and provide to other components.
when I try to add a book via a form it shows TypeError: Object(...) is not a function
below is my book context where I have created a context for addBook, removeBook, and entire BookState and provided it to its wrapping child components
BookContext.js
import React, { createContext, useState } from 'react';
import {uuid} from 'uuid/dist/v1';
export const BookContext = createContext();
const BookContextProvider = ({ children }) => {
const [books, setBooks] = useState([
{ id: 1, title: 'name of wind', author: 'Elizabath' },
{ id: 2, title: 'the narrow bridge', author: 'Mc clary' },
]);
const addBook = (title, author) => {
setBooks([...books, { id: uuid(),title, author }])
};
const removeBook = (id) => {
setBooks(books.filter((book) => book.id !== id))
};
return (
<BookContext.Provider
value={{ books, addBook, removeBook }}
>
{children}
</BookContext.Provider>
);
};
export default BookContextProvider;
below is my bookForm.js file where I have consumed context from the provider and destructured addBook from it
BookForm.js
import React, { useContext, useState } from 'react';
import { BookContext } from '../Contexts/BookContext';
function BookForm() {
const { addBook } = useContext(BookContext);
const [title, setTitle] = useState('');
const [author, setAuthor] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
addBook(title, author)
setTitle('')
setAuthor('')
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Book Name"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
/>
<input
type="text"
placeholder="Author Name"
value={author}
onChange={(e) => setAuthor(e.target.value)}
required
/>
<input type="submit" value="add Book" />
</form>
);
}
export default BookForm;
Your code appears to be working fine. I believe the issue lies in a separate part. I'd suggest following the stack trace of the error as that should give you a better location of the error. Or there's more to the context provider than you included on this.
There might be another component that uses the books that are added that throws when a new book is added.
const { createContext, useContext, useState } = React;
const uuid = uuidv1;
const BookContext = createContext();
const BookContextProvider = ({ children }) => {
const [books, setBooks] = useState([
{ id: 1, title: "name of wind", author: "Elizabath" },
{ id: 2, title: "the narrow bridge", author: "Mc clary" },
]);
const addBook = (title, author) => {
setBooks([...books, { id: uuid(), title, author }]);
};
const removeBook = (id) => {
setBooks(books.filter((book) => book.id !== id));
};
return (
<BookContext.Provider value={{ books, addBook, removeBook }}>
{children}
</BookContext.Provider>
);
};
function BookForm() {
const { addBook } = useContext(BookContext);
const [title, setTitle] = useState("");
const [author, setAuthor] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
addBook(title, author);
setTitle("");
setAuthor("");
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Book Name"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
/>
<input
type="text"
placeholder="Author Name"
value={author}
onChange={(e) => setAuthor(e.target.value)}
required
/>
<input type="submit" value="add Book" />
</form>
);
}
function Books() {
const { books } = useContext(BookContext);
return (
<div>
{books.map(({ id, title, author }) => (
<div key={id}>
{title} - {author}
</div>
))}
</div>
);
}
ReactDOM.render(
<BookContextProvider>
<BookForm />
<Books />
</BookContextProvider>,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuidv1.min.js" integrity="sha512-4hJwRX3o1o5dU3A+ffhCQpLzOT7U0wDoR9Ha7McoxXez011iFJ6RMB08BrzE23G+gB3gNriotihq7TPIf7x/NA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="root" />