So I am trying to make a textfield component in React that is highly reusable but whenever I try to access event.target.name or event.target.value I get empty data.
Is there any way to get this code to work?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name='react'
onChange={e => props.change(e)}
/>
</label>
)
}
return (
<form onSubmit={printValues}>
<TextField
value={form.username}
name='username'
change={updateField}
/>
<br />
<TextField
value={form.password}
name='password'
change={updateField}
/>
<br />
<button>Submit</button>
</form>
);
}
This code is an example that I have gotten to work. Why does this code work but not the code above?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
return (
<form onSubmit={printValues}>
<label>
Username:
<input
value={form.username}
name="username"
onChange={updateField}
/>
</label>
<br />
<label>
Password:
<input
value={form.password}
name="password"
type="password"
onChange={updateField}
/>
</label>
<br />
<button>Submit</button>
</form>
);
}
your child component has it's name prop hardcoded name='react' and that's because your [e.target.name]: e.targe.value statement is not working, use name={props.name} instead and it would solve the problem.
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name={props.name}
onChange={e => props.change(e)}
/>
</label>
)
}
The first code you show has a prop change instead of the onChange event listener in the TextField component.
I was looking here https://material-ui.com/es/components/text-fields/ because I never used material-ui, and in the example they still use the regular onChange.
So try changing change for onChange, as it is the only difference between both code examples besides the use of the component.
Related
I want to work using maltiple values in use state and crud in local storage use state like
const [Data,setData]=[[{
name:'luis',
pass:'1234',
//.......
}]
]
And it updates with the form
<input>
// .......
if value true Display take look at this example I try but I cannot do how to solve it
import './App.css';
import React, { useState,useEffect } from 'react';
function App() {
const [User, setUser] = useState([
{
Name:'',
Pass:'',
Email:'',
}
]);
const [storedUser,setstoredUser]=useState([])
const handle = () => {
localStorage.setItem(JSON.stringfy(...User))
setstoredUser(...User);
};
const remove = () => {
localStorage.removeItem();
};
return (
<div className="App">
<h1>Name of the user:</h1>
<input
placeholder="Name"
name='Name'
value={User.Name}
onChange={(e) => setUser({...User,[e.target.name]:[e.target.value]})}
/>
<h1>Password of the user:</h1>
<input
type="password"
name="Pass"
placeholder="Password"
value={User.Pass}
onChange={(e) => setUser({...User,[e.target.name]:[e.target.value]})}
/>
<h1>Email of the user:</h1>
<input
type="mail"
name="Email"
placeholder="Email"
value={User.Email}
onChange={(e) => setUser({...User,[e.target.name]:[e.target.value]})}
/>
<div>
<button onClick={handle}>Done</button>
</div>
{storedUser.Name && (
<div>
Name: <p>{localStorage.getItem('Name')}</p>
</div>
)}
{storedUser.Pass && (
<div>
Password: <p>{localStorage.getItem('Pass')}</p>
</div>
)}
{storedUser.Email && (
<div>
Password: <p>{localStorage.getItem('Email')}</p>
</div>
)}
<div>
<button onClick={remove}>Remove</button>
</div>
</div>
);
}
export default App;
Here I try how to do this formate I try to all data in state and stringify set this local storage. then remove and display I think explaine on detail
You're missing the key
localStorage.setItem("User",JSON.stringfy(...User))
If you want each key. Loop over they keys and values and set them. As stated by another user, your UserState is an array where it should just be an object
Object.entries(User).forEach(([key,value])=>{
localStorage.setItem(key,value)
})
You are doing a few things incorrectly here:
you are not providing a key for local storage
you don't need to spread objects directly inside setState
use the removeItem method to clear the user from localStorage
you are setting the user as an array to state not an object, this is unnecessary in the example you have
If the goal is to persist a single user between sessions via local storage. Then all you need to do is save the user to local storage when the form is submitted.
Then, when the component loads, check local storage for user data.
import { useEffect, useState } from 'react'
function App() {
const [User, setUser] = useState({
Name: '',
Pass: '',
Email: '',
})
const handle = () => {
const nextUser = JSON.stringify(User)
localStorage.setItem('user', nextUser)
}
const remove = () => {
localStorage.removeItem('user')
}
useEffect(() => {
const storedUser = localStorage.getItem('user')
if (storedUser) {
setUser(JSON.parse(storedUser))
}
}, [])
return (
<div className="App">
<h1>Name of the user:</h1>
<input
placeholder="Name"
name="Name"
value={User.Name}
onChange={(e) => setUser({ ...User, [e.target.name]: e.target.value })}
/>
<h1>Password of the user:</h1>
<input
type="password"
name="Pass"
placeholder="Password"
value={User.Pass}
onChange={(e) => setUser({ ...User, [e.target.name]: e.target.value })}
/>
<h1>Email of the user:</h1>
<input
type="mail"
name="Email"
placeholder="Email"
value={User.Email}
onChange={(e) => setUser({ ...User, [e.target.name]: e.target.value })}
/>
<div>
<button onClick={handle}>Done</button>
</div>
{User.Name && (
<div>
Name: <p>{User.Name}</p>
</div>
)}
{User.Pass && (
<div>
Password: <p>{User.Pass}</p>
</div>
)}
{User.Email && (
<div>
Password: <p>{User.Email}</p>
</div>
)}
<div>
<button onClick={remove}>Remove</button>
</div>
</div>
)
}
export default App
Since User already is an array of user objects I wouldn't spread them in your handle function. You also need to set a key if you're saving in localStorage. This is how to save your list of users under "users":
const handle = () => {
localStorage.setItem("users", JSON.stringfy(User));
setstoredUser(User);
};
Now when retrieving users from the localStorage you can make use of JSON.parse like this:
users = JSON.parse(localStorage.getItem("users") || "[]");
And for deletion you would need the key "users" here as well:
localStorage.removeItem("users");
I am creating a simple login form and I am saving the users information to state as they type. However, the value does not register to state as expected.
Here is the user state
const [user, setUser] = useState({
firstName: '',
lastName: '',
email: ''
});
Here is the input component
export function Input({ id, placeholder, autoComplete, type, name, label, value, handleInputChange }) {
return (
<div className="form-input">
<label className="form-label" htmlFor={id}>{label}</label>
<input
placeholder={placeholder}
autoComplete={autoComplete}
id={id}
type={type}
name={name}
value={value}
onChange={handleInputChange}
/>
</div>
)
}
Here is the handleInputChange function that is passed into the input component
function handleInputChange(event) {
const { name, value } = event.target;
setUser({ ...user, [name]: value });
}
Here is how one of the input components is used in the parent component
<Input
id="first-name"
placeholder="Charlie"
autoComplete="given-name"
type="text"
name="firstName"
label="First Name"
value={user.firstName}
handleInputChange={handleInputChange}
/>
Here are some resources I've looked at thus far:
https://reactjs.org/docs/hooks-reference.html#usestate
onChange not updating React
https://daveceddia.com/usestate-hook-examples/
I'd suggest to create one state per input field:
export function Form({ addItem }) {
const [name, setName] = useState('');
return (
<form>
<Input
id="first-name"
placeholder="Charlie"
autoComplete="given-name"
type="text"
name="firstName"
label="First Name"
value={user.firstName}
handleInputChange={(event) => setName(event.target.value)}
/>
</form>
);
}
I currently have a working hook, that checks the onChange of input fields, then spreads (I think that is the term for ...) them into a variable and send's them to a Lamda function to send a email with sendgrid. All of that is working.
However if I add a input type of checkbox I am a little lost on the logic side. Because I am checking for the target's name and pairing that with it's value. Well the checkbox has a checked state not a value.
I assume I have to do something like e.target.checked in my setFormState however how do I do that as well as the e.target.value and still spread it to ...formState?
I am a react noob.
Here is my react state and the change event and submit event
const [formState, setFormState] = React.useState({
name: "",
package: `${data.datoCmsPricing.title}`,
email: "",
subject: "",
weightLoss:"",
message: "",
})
const onChange = (e) => {
setFormState({...formState, [e.target.name]: e.target.value });
}
const submitForm = async (e) => {
e.preventDefault();
console.log("test");
try{
const response = await fetch("/.netlify/functions/sendmail", {
method: "POST",
body: JSON.stringify(formState),
})
if (!response.ok) {
console.log(response);
return
}
console.log("success email");
} catch(e){
console.log("error");
}
}
Here is my form code
<form onSubmit={submitForm}>
<label>
Name
<input
type="text"
name="name"
value={formState.name}
onChange={onChange}
/>
</label>
<label>
Email
<input
type="email"
name="email"
value={formState.email}
onChange={onChange}
/>
</label>
<label>
Subject
<input
type="text"
name="subject"
value={formState.subject}
onChange={onChange}
/>
</label>
<div>
<h3>Reasons for wanting to train</h3>
<label>
Weight Loss
<input
type="checkbox"
name="weightLoss"
checked={formState.weightLoss}
onChange={onChange}
/>
</label>
</div>
<label>
message
<textarea
name="message"
value={formState.message}
onChange={onChange}
/>
</label>
<button type="submit">Submit</button>
</form>
In your change handler, detect if it's a checkbox that is not checked, then set the state accordingly:
if (e.target.type === 'checkbox' && !e.target.checked) {
setFormState({...formState, [e.target.name]: ''});
} else {
setFormState({...formState, [e.target.name]: e.target.value });
}
I'm having a syntax doubt on how to update React state using hooks in 2 situations.
1) I have a state called company and a form that fills it up. In contact section, there are two inputs referring to the company employee (name and telephone number). But if the company has more than one employee to be contacted, there is an "Add More Contact" button, which must duplicate the same inputs (of course, aiming to a second contact). How can I do that? I mean, to generate another index in the array "contacts" inside the state, increment the totalOfContacts inside the object that has that array and create the input tags so user can type the second contact's data?
2) When I type any inputs, the code triggers the handleChange function. The "name" and "city" already update the state because they are simple states. But how can I update the contact name and his telephone number, since they are part of an index of an array inside the state?
The code below is already working and my 2 questions are exactly the two commented lines (lines 20 and 29).
The "Save" button simply console.log the results so we can monitor them.
Thanks for now.
import React, { useState, useEffect } from "react";
export default () => {
const [company, setCompany] = useState({
name: "", city: "",
contact: {
totalOfContact: 1,
contacts: [
{id: 0, contactName: "", telephoneNumber: ""}
]
}
})
useEffect(() => {
console.log("teste");
})
const handleChange = item => e => {
if (item === "contactName" || "telephone") {
// How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
} else {
setCompany({ ...company, [item]: e.target.value })
}
}
const handleClick = (e) => {
e.preventDefault();
if (e.target.value === "add") {
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
} else {
console.log(`The data of the company is: ${company}`);
}
}
return (
<div>
<form>
<h3>General Section</h3>
Name: <input type="text" onChange = {handleChange("name")} value = {company.name} />
<br />
City: <input type="text" onChange = {handleChange("city")} value = {company.city} />
<br />
<hr />
<h3>Contacts Section:</h3>
Name: <input type="text" onChange = {handleChange("contactName")} value = {company.contact.contacts[0].name} />
Telephone Numer: <input type="text" onChange = {handleChange("telephone")} value = {company.contact.contacts[0].telephoneNumber} />
<br />
<br />
<button value = "add" onClick = {(e) => handleClick(e)} >Add More Contact</button>
<br />
<br />
<hr />
<button value = "save" onClick = {(e) => handleClick(e)} >Save</button>
</form>
</div>
)
}
To update the state value, you can use functional setState,
const handleChange = item => e => {
//Take the value in a variable for future use
const value = e.target.value;
if (item === "contactName" || "telephone") {
setCompany(prevState => ({
...prevState,
contact: {...prevState.contact, contacts: prevState.contact.contacts.map(c => ({...c, [item]: value}))}
}))
} else {
setCompany({ ...company, [item]: e.target.value })
}
}
To add new set of input on the click of button you can do this,
const handleClick = (e) => {
e.preventDefault();
//This is new set of input to be added
const newSetOfInput = {id: company.contact.contacts.length, contactName: "", telephoneNumber: ""}
if (e.target.value === "add") {
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
setCompany(prevState => ({
...prevState,
contact: {...prevState.contact, contacts: prevState.contact.contacts.concat(newSetOfInput), totalOfContact: prevState.contact.contacts.length + 1}
}))
} else {
console.log(`The data of the company is: ${company}`);
}
}
Finally you need to iterate over your contacts array like,
{company.contact.contacts && company.contact.contacts.length > 0 && company.contact.contacts.map(contact => (
<div key={contact.id}>
Name: <input type="text" onChange = {handleChange("contactName")} value = {contact.contactName} />
<br/>
Telephone Numer: <input type="text" onChange = {handleChange("telephoneNumber")} value = {contact.telephoneNumber} />
</div>
))}
Demo
Note: You should use block elements like div instead of breaking the line using <br/>
To answer your question let us scope down this problem to a much simpler problem, which is how to handle array of contacts.
You just need know the following things:
Map function
How to update array without mutating the original array
I'll use TypeScript so you can understand better.
const [state, setState] = React.useState<{
contacts: {name: string}[]
}>({contacts: []})
return (
<div>
{state.contacts.map((contact, index) => {
return (
<div>
Name:
<input value={contact.name} onChange={event => {
setState({
...state,
contacts: state.contacts.map((contact$, index$) =>
index === index$
? {...contact$, name: event.target.value}
: {...contact$}
)
})
}}/>
</div>
)
}}
</div>
)
Also, this kind of problem is fairly common in React, so understand and memorize this pattern will help you a lot.
You can do something like this.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [company, setCompany] = useState({
name: "",
city: "",
contact: {
totalOfContact: 1,
contacts: [{id: 0, contactName: "", telephoneNumber: ""}]
}
});
console.log(company);
useEffect(() => {
console.log("teste");
}, []);
const handleChange = (item, e,index) => {
if (item === "contactName" || item === "telephoneNumber") {
const contactsNew = [...company.contact.contacts];
contactsNew[index] = { ...contactsNew[index], [item]: e.target.value };
setCompany({
...company,
contact: { ...company.contact, contacts: contactsNew }
});
// How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
} else {
setCompany({ ...company, [item]: e.target.value });
}
};
const handleClick = e => {
e.preventDefault();
if (e.target.value === "add") {
const contactNew = {...company.contact};
contactNew.totalOfContact = contactNew.totalOfContact + 1;
contactNew.contacts.push({id:contactNew.totalOfContact -1, contactName: "", telephoneNumber: ""});
setCompany({...company, contact: {...contactNew}});
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
} else {
alert("Push company to somewhere to persist");
console.log(`The data of the company is: ${company}`);
}
};
return (
<div>
<form>
<h3>General Section</h3>
Name:{" "}
<input
type="text"
onChange={(e) => handleChange("name", e)}
value={company.name}
/>
<br />
City:{" "}
<input
type="text"
onChange={(e) => handleChange("city", e)}
value={company.city}
/>
<br />
<hr />
<h3>Contacts Section:</h3>
{company.contact.contacts.map((eachContact, index) => {
return <React.Fragment>
Name:{" "}
<input
type="text"
onChange={(e) => handleChange("contactName",e, index)}
value={eachContact.name}
/>
Telephone Numer:{" "}
<input
type="text"
onChange={(e) => handleChange("telephoneNumber",e, index)}
value={eachContact.telephoneNumber}
/>
<br />
</React.Fragment>
})}
<br />
<button value="add" onClick={e => handleClick(e)}>
Add More Contact
</button>
<br />
<br />
<hr />
<button value="save" onClick={e => handleClick(e)}>
Save
</button>
</form>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Your state structure looks like an ideal candidate for useReducer hook. I would suggest you try that instead of useState. Your code should look muck readable that way, I suppose. https://reactjs.org/docs/hooks-reference.html#usereducer
I want create a function using with i can reset value in form inputs without submit. I tried create that function in App Component (resetFormFields) and pass it on props to Form Component. It's preety simply when I want to do this onSubmit (e.target.reset()) but I got stuck when I have to do it without submit, on a different element than the form. Can I do that without adding these values to state?
App:
class App extends Component {
state = {
people: [],
formMessages: [],
person: null
};
handleFormSubmit = e => {
e.preventDefault();
const form = e.target;
const name = form.elements["name"].value;
const username = form.elements["username"].value;
this.addPerson(name, email);
form.reset();
};
resetFormFields = () => {
return;
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit}
reset={this.resetFormFields} />
</div>
);
}
Form:
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
onHandleFormSubmit = (e) =>{
e.preventDefault();
e.target.reset();
}
You need to make your inputs controlled by passing the value you store in your state then you just have to reset the state values and your component value resets.
check this sample below
handleInputChange = (e) => {
let { name, value } = e.target;
this.setState({
...this.state,
inputs: {
[name]: value
}
});
}
your component will now look like
<input name='fullName' value={this.state.inputs.fullName} onChange={this.handleInputChange} />
Your reset function will just clear the state and your input field will be empty since it's controlled via state
resetInputFields = () => {
this.setState({ inputs: {} })
}
you should give set your input values based on component state, then just update the component state
class App extends Component {
state = {
people: [],
formMessages: [],
person: null,
name: "",
email: "",
};
updateState = (newState) => {
this.setState(newState);
}
handleFormSubmit = e => {
e.preventDefault();
this.addPerson(this.state.name, this.state.email);
form.reset();
};
resetFormFields = () => {
this.setState({name:"", email: ""});
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit} updateState={this.updateState}
reset={this.resetFormFields} email={this.state.email} name={this.state.name} />
</div>
);
}
and then
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
value={this.props.name}
onChange={(e) => this.props.updateState({name: e.target.value})}
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
value={this.props.email}
onChange={(e) => this.props.updateState({email: e.target.value})}
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );