I want to not loose my other states, when changing one in functional component. I know how to do it in class components, but how you do it in functional? Class component this.setState({...this.state, somestate: somestate});
My Functional component looks like this now:
const [product, setProduct] = useState({});
let { id } = useParams();
useEffect(() => {
let productFromLocalStorage = localStorage[id];
setProduct(JSON.parse(productFromLocalStorage));
}, [id]);
function handleName(event) {
setProduct({name: event.target.value});
// setName(event.target.value);
}
return (
<div style={styles.container}>
<h1 style={styles.title}>Product {product.name} Edit Page</h1>
<form style={styles.form} onSubmit={handleSave}>
<label style={styles.label}>
Name: <input value={product.name} type="text" onChange={handleName}></input>
</label>
<label style={styles.label}>
EAN: <input value={product.ean} type="text" onChange={handleEan}></input>
</label>
<label style={styles.label}>
Type: <input value={product.type} type="text" onChange={handleType}></input>
</label>
<label style={styles.label}>
Weight:{' '}
<input value={product.weight} type="text" onChange={handleWeight}></input>
</label>
<label style={styles.label}>
Color:{' '}
<input value={product.color} type="text" onChange={handleColor}></input>
</label>
<label style={styles.label}>
Active:{' '}
<input value={product.active} type="checkbox" onChange={handleActive}></input>
</label>
<input style={styles.submitButton} type="submit" value="SAVE"></input>
</form>
</div>
);
function handleSave(event) {
const productInfo = {
name: product.name,
ean: product.ean,
type: product.type,
weight: product.weight,
color: product.color,
active: product.active,
};
localStorage.setItem(id, JSON.stringify(productInfo));
alert('Product: ' + name + ' edited!');
}
my product state has other params like weight and etc... when I am setting setProduct({name: event.target.value}); other values gets empty. How to solve it?
what you are doing is overriding so like you said you need to keep current values so do this:
setProduct({...product, name: event.target.value})
You can do the same via useState() also,
const [product, setProduct] = useState({});
someHandler=(event)=>(
setProduct(product=>{
...product,
name:event.target.value
});
)
Related
I'm having some trouble pushing the values from my form to an array that I'm mapping on screen.
const ForumTopic = [
{
title: "First Post",
messages: "test",
author: "Dagger",
count: 1,
date: "02/16",
},
];
const [topic, setTopic] = useState(ForumTopic);
Storing ForumTopic in state so I can add entries and display on screen after I click the submit button below.
const addTopic = (e) => {
e.preventDefault();
setTopic([...topic, e.target.value]);
};
<form onSubmit={addTopic}>
Create a topic title
<label htmlFor="title">
<input id="title"></input>
</label>
Write your message
<label htmlFor="message">
<textarea id="message"></textarea>
</label>
<label htmlFor="author">
<input id="author" defaultValue="Dagger" hidden></input>
</label>
<label htmlFor="count">
<input id="count" defaultValue="1" hidden></input>
</label>
<label htmlFor="date">
<input id="date" defaultValue="02/16/2023" hidden></input>
</label>
<button type="submit">
Post New Message
</button>
</form>
That's my code and form. The code is meant to push the values from each label in the form to create a new object inside the topic array. I want everything stored in a new object with the id of each label to match the names of each object (title, author, date, etc) but for some reason all I'm getting are undefined errors.
A simple way to do it is like this.
You need to obtain the value you are getting with an onChange in the input.
LINK to the example: https://stackblitz.com/edit/react-8r9f8l?file=src%2FApp.js
import React, { useState } from 'react';
const ForumTopic = [
{
title: 'First Post',
messages: 'test',
author: 'Dagger',
count: 1,
date: '02/16',
},
];
export default function App() {
const [topic, setTopic] = useState(ForumTopic);
const [inputObj, setInputObj] = useState({
title: '',
messages: '',
author: 'Dagger',
count: 1,
date: '02/16',
});
const handleChange = (event) => {
setInputObj((curr) => ({
...curr,
[event.target.name]: event.target.value,
}));
};
const addTopic = (e) => {
e.preventDefault();
setTopic([...topic, inputObj]);
};
return (
<>
<form onSubmit={addTopic}>
<label htmlFor="title">
Create a topic title
<input
id="title"
name="title"
value={inputObj.title}
onChange={handleChange}
></input>
</label>
<label htmlFor="message">
Write your message
<textarea
id="message"
name="messages"
value={inputObj.messages}
onChange={handleChange}
></textarea>
</label>
<label htmlFor="author">
<input id="author" name="author" defaultValue="Dagger" hidden></input>
</label>
<label htmlFor="count">
<input id="count" name="count" defaultValue="1" hidden></input>
</label>
<label htmlFor="date">
<input id="date" name="date" defaultValue="02/16/2023" hidden></input>
</label>
<button type="submit">Post New Message</button>
</form>
{topic.map((item) => {
return (
<>
<p>{item.title}</p>
<p>{item.messages}</p>
<p>{item.author}</p>
<p>{item.count}</p>
<p>{item.date}</p>
<span>------------</span>
</>
);
})}
</>
);
}
The problem is that on your addTopic function:
e.target.value are always undefined
to access the data you have to do:
const addTopic = (e) => {
e.preventDefault()
const myData = {
title: e.target.title.value,
message: e.target.message.value
}
setTopic(prev => [...prev, myData])
}
i'm a beginner in React and trying to learn useState. and I have difficulties on how to get the value of input and to save the value and print it on button click
const HomePage = () => {
const [state, setState] = useState({
Name: "",
surName: "",
});
const handleChange = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};
const RenderNameOC = () => {
return (
<p>
Halo {Name} {surName}
</p>
);
};
return (
<DivContainer>
<ContainerTitle>
<p>Exercise 2 - Form</p>
</ContainerTitle>
<InputContainer>
<InputArea>
<label>Name: </label>
<input type="text" value={state.Name} onChange={handleChange} />
</InputArea>
<InputArea>
<label>Surname: </label>
<input type="text" value={state.surName} onChange={handleChange} />
</InputArea>
<SubmitButton onClick={RenderNameOC}>Submit</SubmitButton>
</InputContainer>
</DivContainer>
);
};
export default HomePage;
this is my code right now and the error it gave me was 'name' and 'surname' is not defined.
my expected result is that there will be 2 input textbox with for name and surname. and when the button is clicked, it will add a new <p> below it.
Here should be state.Name and state.surName
<p>
Halo {state.Name} {state.surName}
</p>
And add name in both inputs
<input
type="text"
name="Name"
value={state.Name}
onChange={handleChange}
/>
<label>Surname: </label>
<input
type="text"
name="surName"
value={state.surName}
onChange={handleChange}
/>
But no point of returning anything RenderNameOC since onClick is a void function. Just move this template below the submit button
Demo
I am messing around with Riot's API that allows getting information of a player by the player's name. I am trying to get an API key (I only got a 24 hour key) and the target player's name from users' input.
export function PlayerSearch() {
function handlesubmit(e) {
console.log(e.target);
e.preventDefault();
}
return (
<div className='player'>
<div className='inputfield'>
<form onSubmit={handlesubmit} method='GET' autoComplete="off">
<div>
<label htmlFor="key">Your API key:</label>
<input
placeholder='Your API key'
onFocus={(e)=>{e.target.placeholder=''}}
type="text"
id="key"
name="key" />
</div>
<div>
<label htmlFor="name">Player name:</label>
<input
placeholder='Player name'
onFocus={(e)=>{e.target.placeholder=''}}
type="text"
id="name"
name="name" />
</div>
<div>
<input type='submit' />
</div>
</form>
</div>
</div>
);
}
And I got this in the console:
So how exactly do I extract the two inputs from the form?
In addition, is it possible that I can call another component and pass data as props to handle the submitted data so that the code is cleaner?
You should have a state to store your inputs:
const [formData, setFormData] = useState({ key: "", name: "" });
Then you need a function that gets called onChange of your input fields to update your state:
const onChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
And you need to pass that function to your input onChange property:
<input
placeholder="Your API key"
onFocus={(e) => {
e.target.placeholder = "";
}}
type="text"
name="key"
value={formData.key}
onChange={onChange}
/>
Then you can access the state in your handleSubmit:
function handlesubmit(e) {
console.log(formData);
e.preventDefault();
}
I'm trying to send data from inputs to redax storage. Why does the dispatch(addChild(parent)) get an error?
Error: Invalid hook call. Hooks can only be called inside of the body of a function component
import {useDispatch} from "react-redux";
import { addChild} from "../../store/actions/actions";
const Form = () => {
const [parent, setParent] = useState([{name: "", age: ""}]);
const [childList, setChildList] = useState([{name: "", age: ""}])
const dispatch = useDispatch;
const handleChange = (e, index) => {
const { name, value } = e.target;
const child = [...childList];
child[index][name] = value;
setChildList(child)
}
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addChild(parent))
}
const addChildElement = ()=> {
setChildList( [...childList, {name: "", age: ""}]);
}
return (
<form className="form" onSubmit={handleSubmit}>
<div className="form__parent">
<div className="form-title">Персональные данные</div>
<div className="form-item">
<input className="form-input" type="text"
value={parent.name}
onChange={(e) => setParent({...parent, name: e.target.value})}
/>
<label className="form-label">Имя</label>
</div>
<div className="form-item">
<input className="form-input" type="number"
value={parent.age}
onChange={(e) => setParent({...parent, age: e.target.value})}
/>
<label className="form-label">Возраст</label>
</div>
</div>
<div className="form__child">
<div className="form__child-head">
<div className="form-title">Дети (макс.5)</div>
<button className="btn add-child-btn" onClick={addChildElement}>Добавить ребенка</button>
</div>
<ul className="form__child-content">
{
childList.map((value, id) => {
return (
<li className="child-list" key={id}>
<div className="form-item">
<input className="form-input" type="text"
name="name"
value={value.name}
onChange={e => handleChange(e, id)}
/>
<label className="form-label">Имя</label>
</div>
<div className="form-item">
<input className="form-input" type="number"
value={value.age}
name="age"
onChange={e => handleChange(e, id)}
/>
<label className="form-label">Возраст</label>
</div>
<button className="remove-list">Удалить</button>
</li>
);
})
}
</ul>
<input className="btn submit" type="submit" value="Сохранить" />
</div>
</form>
);
}
export default Form;`
This is the mistake you have made. call brackets() are missing in your useDispatch declaration.
it should be corrected like below
const dispatch = useDispatch();
Error is totally valid because now your dispatch is not the output of useDispatch. It's useDispatch itself and error is due to useDispatch hook is used inside handleSubmit.
I am trying to type on the inputs but it is not allowing me too. My state changes but it doesn't show.
I am using props to show an event OR and empty event if there is no props (no event selected).
Im sorry but Stack is telling me to add more details but I don't know what else to add, I already described my problem
class EventForm extends PureComponent {
state = {
event: this.props.selectedEvt,
query: ""
};
onFormSubmit = e => {
e.preventDefault();
this.props.addEvent(this.state.event);
};
onInputChange = evt => {
console.log(evt);
evt.persist();
let newEvt = this.state.event;
newEvt[evt.target.name] = evt.target.value;
this.setState({
event: newEvt
});
};
componentDidUpdate(prevProps) {
this.props.selectedEvt &&
this.setState({
event: this.props.selectedEvt
});
}
render() {
const { event } = this.state;
return (
<form className="card" onSubmit={this.onFormSubmit}>
<div className="form-row card-body">
<div className="form-group col-md-12">
<label hmtlfor="inputName">Event Name</label>
<input
name="title"
type="text"
className="form-control"
id="inputEventName"
onChange={this.onInputChange}
value={event.title}
/>
<label hmtlfor="inputDate">Event Date</label>
<input
name="date"
type="date"
className="form-control"
id="inputEventDate"
onChange={this.onInputChange}
value={event.date}
/>
<label hmtlfor="inputDate">Event Time</label>
<input
name="time"
type="time"
className="form-control"
id="inputEventTime"
onChange={this.onInputChange}
value={event.time}
/>
<label hmtlfor="inputAddress">Address</label>
<input
name="address"
type="text"
className="form-control"
id="autocomplete"
onChange={this.onInputChange}
value={event.address}
autoComplete="new-password"
/>
<label hmtlfor="inputHost">Hosted By</label>
<input
name="host"
type="text"
className="form-control"
id="inputHost"
onChange={this.onInputChange}
value={event.host}
/>
<label hmtlfor="inputDesc">Description</label>
<textarea
name="desc"
className="form-control"
rows="5"
id="inputDesc"
wrap="soft"
onChange={this.onInputChange}
value={event.description}
/>
<button type="submit" className="btn btn-primary mt-2">
Submit
</button>
</div>
</div>
</form>
);
}
}
export default EventForm;
Every time input value change componentDidMount run and you reset state to initial state value in componentDidUpdate.
componentDidUpdate(prevProps) {
this.props.selectedEvt &&
this.setState({
event: this.props.selectedEvt // Here is the problem
});
}
Also You mutate state when input change. And because its pureComponent it will not update.
Change onInputChange to
onInputChange = evt => {
let name = evt.target.name;
let value = evt.target.value;
let newEvent = {...this.state.event};
newEvent[name] = value
this.setState({event: newEvent});
}