I have a list of columns, they are rendered as TextFields:
{columns.map((column, index) => (
<TextField
key={index}
margin="dense"
id={column}
label={column}
name={column}
type="text"
onChange={this.handleInputChange}
fullWidth/>
))}
and handleInputChange function is written as below:
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
addData: {
[this.state.fullName]:{
[name]: value
}
}
});
}
The data that I want to receive is:
addData: {
[name-of-column-1]: value,
[name-of-column-2]: value,
[name-of-column-3]: value,
.......
}
but the handleInputChange function overrides after every TextField change, the data that I received:
addData: {
[name-of-column-1 (or 2, 3...)]: value,
}
Is there any way to get the data I need? Thanks everyone!
How do I setState for nested object?
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
addData: {
[this.state.fullName]:{
...this.state.addData[this.state.fullName],
[name]: value,
}
}
});
}
You are assigning whole new object every time value from one TextField changes and not retaining old values from state.
I'm not sure what exactly is this.state.fullName, but in order to have your desired state structure you can implement it like this:
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
addData: {
...this.state.addData,
[name]: value
}
});
}
When destructing this.state.addData (with ...), you are basically assigning all of its properties to the new object and then adding additional [name]: value. Its important to put destruction above new assignment in order to update latest value.
I think you should prepare your data first and set it into your state or try in this way
this.setState({
addData: {
[...this.state.fullName]:{
[name]: value
}
}
});
This's a issue when you setState. When you using
this.setState({
addData: {
[this.state.fullName]:{
[name]: value
}
}
});
that mean you are setting a new state just have one nested object
addData { abc: value }
so the old values are lost. You need add them before you change the new value. Try this
handleInputChange(event) {
const { name, value } = event.target;
const newData = {...this.state.addData};
newData[name] = value;
this.setState({ addData: newData });
}
or
handleInputChange(event) {
const { name, value } = event.target;
this.setState({
addData: {
...this.state.addData,
[name]: value
}
});
}
Related
I have a function with code:
const { name, value, } = e.currentTarget;
this.setState({ [name]: value })
How can I make this construction with useState hook?
It's unpossible with useState since its modyfing function is stored in variable.
const [state, setState] = useState(initialState);
const { name, value, } = e.currentTarget;
const values = {[name]: value}
setState(prevState => {
return {...prevState, ...values};
});
I am trying to update the input value. It's working fine, it's updating its value. However, there is a problem, when it's updated its value, it's converting to string from number. I have already tried with Number, parseInt, it does not work. it always converts to string.
import React from 'react';
import useForm from '../lib/useForm';
const CreateProduct = () => {
const { input, handleChange } = useForm({
name: 'mama',
price: 0,
});
return (
<form>
<label htmlFor="price">
Price
<input
type="number"
id="price"
name="price"
placeholder="Price"
value={parseInt(input.price)}
onChange={handleChange}
/>
</label>
</form>
);
};
export default CreateProduct;
and here in the custom function:
import * as React from 'react';
export default function useForm(initialValue = {}) {
const [input, setInput] = React.useState(initialValue);
function handleChange(event) {
const { name, value } = event.target;
setInput({
...input,
[name]: value,
});
}
return {
input,
handleChange,
};
}
and here is the initial state:
and here is the updated state with the number gotcha
Issue
Inputs deal only in strings. If you want the value to be anything other than you should convert it when passing to the handler, or in the handler itself.
Solution
Convert the value back to a number when processing in handler
function handleChange(event) {
const { name, type, value } = event.target;
setInput(input => {
const nextInput = {...input}
switch(type) {
case 'number':
nextInput[name] = Number(value);
break;
// handle other input type cases
default:
nextInput[name] = value;
}
return nextInput;
});
}
Have you tried changing it that way?
setInput({
...input,
[name]: name === "price" ? parseInt(value) : value,
});
This check whether name 's type is number or not. When it is number, parse it to Number type to prevent it be converted to string. If not, just update the input by what it is originally pass in.
setInput({
...input,
[name]: typeof name === 'number' ? Number(value) : value,
});
So, I define 2 states with the same array, it's listRasioUom and showListRasioUom. but when I setstate to listRasioUom state, the showListRasioUom state also changed with the same value as listRasioUom. any help? this is my code
handleChange = (e) => {
if (["rasio", "uom"].includes(e.target.name)) {
let listRasioUom = this.state.listRasioUom
if(listRasioUom.some(el => el.uom === e.target.value))
{
alert('Duplicate uom')
}
else
listRasioUom[e.target.dataset.id][e.target.name] = e.target.value;
this.setState({ listRasioUom })
}
}
}
showListRasioUom state is used for fetching the data on my datatables, while listRasioUom state is used to modified the state, so i only want to change listrasiouom state, but when i use setstate on listRasioUom state, the showListRasioUom also change.
<MDBDataTable
info={false}
paging={false}
searching={false}
striped
bordered
small
data={{columns: this.state.columns, rows: this.state.showListRasioUom}}
noBottomColumns
/>
Object and arrayss in javascript are assigned by reference and if you mutate the original array the other array which also holds the same reference is also updated
You should update your data in an immutable manner
handleChange = (e) => {
if (["rasio", "uom"].includes(e.target.name)) {
let listRasioUom = this.state.listRasioUom
if(listRasioUom.some(el => el.uom === e.target.value))
{
alert('Duplicate uom')
}
else {
const id = e.target.dataset.id;
const {name,value} = e.target;
this.setState(prev => ({
listRasioUom: prev.listRasioUom.map((item, i) => {
if(i == id) return {...item, [name]: value};
})
return item;
}))
}
}
}
I've got two select components and I want to set values from this selectors to state using one handleChange method. I've tried to do like this:
handleChange = event => {
const { name, value } = event.target;
if (name === 'City') {
this.setState({ selectedCity: value });
} else if (name === 'Theatre') {
this.setState({ selectedTheatre: value });
}
};
But I want more universal solution, like I did with inputs:
handleChange = event => {
const { name, value } = event.target;
this.setState({
[name]: value
});
};
My state
this.state = {
cities: [],
theatres: [],
movies: [],
selectedCity: 1,
selectedTheatre: 1
};
at the first, the name for each input must look like the element in the state
ex
tell me
handaleChange=(event)=>{
this.setState({
[event.name]=event.target.value
}
}
tell me if it work or not
make Handel different for selected Combobox element different
handelSelectedElement=(event)=>{
const value=event.target.checked
const name:event.target.name;
let cityselected=Object.assign({},this.state.city,{[name]:value})
this.setState({city:cityselected})}
tell me if work or not
Ensure your dropdown list name matches the state name
this.state = {
cities: [],
theatres: [],
movies: [],
selectedCity: 1,
selectedTheatre: 1
};
<select name="selectedCity"
value={this.state.selectedCity}
onChange={this.handleChange }>
</select>
<select
name="selectedTheatre"
value={this.state.selectedTheatre}
onChange={this.handleChange }>
</select>
handleChange = event => {
const target = event.target
const value = target.type === 'checkbox' ? target.checked : target.value
const name = target.name
this.setState({
[name]: value
});
};
Note: Dont forget to copy your event data into a variable or call event.persist() to avoid issues.
See link [Event target is null inside functional setState
My parent component has a property in its state called formIsValid, initially set to false. My form also has a submit button. I want the submit button to be disabled until after some input fields (a first name and a last name input) have data in them.
This is what my state looks like:
state = {
employees: [],
costEstimates: emptyCosts(),
relationshipOptions: [],
newEmployee: emptyEmployee(),
formIsValid: false
};
This function handles changes to the First and Last name inputs:
// handle input into "First Name" and "Last Name" inputs
handleChangeValue = async e => {
const newEmployee = { ...this.state.newEmployee };
newEmployee[e.currentTarget.name] = e.currentTarget.value;
this.setState({ newEmployee });
this.validateIfCanBeSubmitted();
await this.updateCostsData(newEmployee); // this is an api thing, not relevent
};
This is what sets the formIsValid property in the state. This property is sent as a prop to the Submit button.
validateIfCanBeSubmitted = () => {
const { firstName, lastName } = this.state.newEmployee;
let formIsValid = firstName && lastName ? true : false;
this.setState({ formIsValid });
};
The Submit button for this is correctly getting disabled if the employee property in the state has its first and last names as empty. The problem is that it's "off by 1 update." It's as if the props aren't getting propagated down to the child button component until after the NEXT time the state changes. Here's a gif of the issue:
This is what the child component looks like. It's just a regular HTML button, however it's within a Stateless Functional Component, so the issue is not with the component's state:
<button
type="button"
onClick={onSubmit}
className={'btn btn-primary mr-1 ' + (formIsValid ? '' : 'disabled')}
disabled={!formIsValid}
>
setState() is asynchronous!
this.validateIfCanBeSubmitted(); is executed on the old state; this update this.setState({ newEmployee }); has not been propagated to this.state when your function is executed.
Make validateIfCanBeSubmitted an update-function.
validateIfCanBeSubmitted = ({ newEmployee: { firstName, lastName }}) => {
return {
formIsValid: firstName && lastName ? true : false
};
}
and use it accordingly:
handleChangeValue = async e => {
const {name, value} = e.currentTarget;
const newEmployee = {
...this.state.newEmployee,
[name]: value
};
this.setState({ newEmployee });
this.setState(this.validateIfCanBeSubmitted);
// this is an api thing, not relevant
await this.updateCostsData(newEmployee);
};
Actually, the code in handleChangeValue should also be in such a function, as it uses the previous state to compute the new one.
so how about combining them:
handleChangeValue = e => {
const {name, value} = e.currentTarget;
this.setState((state) => {
const newEmployee = {
...this.state.newEmployee,
[name]: value
};
const { firstName, lastName } = newEmployee;
const formIsValid = firstName && lastName ? true : false;
//and since you never use the returned Promise, why make anything async?
this.updateCostsData(newEmployee);
return { newEmployee, formIsValid };
});
};