Problem in React typing text in a field text - javascript

This is how I have the code to save the changes
const handleChange = (e) => {
let { Nombre, value } = e.target;
setState({ ...state, [Nombre]: value });
};
And in the text field I have this: The problem here is when put the field "Value", when I give the value="Nombre" it makes it impossible to write in the text field.
<Form.Group>
<Form.Label style={{textAlign:"left"}}>Nombre</Form.Label>
<Form.Control
type="text"
placeholder="Nombre"
name="Nombre"
value="Nombre"
onChange={handleChange}
/>
</Form.Group>

Issue
The issue is that you are not destructuring the correct event property to update state.
Solution
Destructure the name and value event properties. You'll also want to be in the habit of using functional state updates to update the state and avoid issues related to stale state enclosures.
Example:
const handleChange = (e) => {
const { name, value} = e.target;
setState(state => ({
...state,
[name]: value
}));
};

Related

Can't edit pre-populated input field data using React

I'm pre-populating an input field, using the data from an API. It's showing up just fine but I can't seem to edit it as it's not possible to edit the "value" field of an input field.
Here's what I want to do:
I want to pre-populate the input fields, edit the data inside them and then push the updated data back to the API.
For example, for the input field with the name Street Address, it's pre-populating the value from the API. In this case "Manhattan". I want to then change that value inside the input field to "New York" and send it back to the API, so that inside the JSON file it will update this specific value.
The left input field from the image is not pre-populating data from the API. Thus, it's empty.
Here's what I've got now.
function WarehousesDetailsEdit() {
const { id } = useParams();
const [warehouseData, setWarehouseData] = useState([]);
const [userInput, setUserInput] = useState([]);
const handleChange = (e) => {
setUserInput((recentInput) => ({ ...recentInput, [e.target.name]: e.target.value }));
};
useEffect(() => {
axios
.get(`http://localhost:8080/warehouses/${id}`)
.then((resp) => {
setWarehouseData(resp.data.warehouseDetails[0]);
})
.catch((err) => {
console.log(err, "Error!");
});
}, [id]);
return (
<>
// here, I'm able to update the input field as it's not coming from the API
<input
name="Warehouse Name"
value={userInput.WarehouseName}
onChange={handleChange}
/>
// here, the field was pre-populated using the API. Meaning, I can't update it.
<input
name="Street Address"
value={warehouseData.name}
onChange={handleChange}
/>
</>
)
I've not included the axios.put() in the code as it doesn't seem relevant right now as I just want to be able to update the pre-populated input field for now.
You're explicitly setting the value:
value={warehouseData.name}
And then nothing ever changes what's in warehouseData. Contrast that with the other <input> where the onChange event changes the value.
You can approach this in a couple of ways. Probably the simplest is to not have a warehouseData state at all. Just update the userInput state with the "pre-populated value". For example:
.then((resp) => {
setUserInput((recentInput) => ({ ...recentInput, name: resp.data.warehouseDetails[0].name }));
})
Then both <input> elements can just use userInput:
<input
name="Street Address"
value={userInput.name}
onChange={handleChange}
/>
Alternatively, if you want to keep both objects in their own separate state for some other reason, then you'd need to update that state. You can create a separate change handler for that:
const handleWarehouseDataChange = (e) => {
setWarehouseData((recentData) => ({ ...recentData, [e.target.name]: e.target.value }));
};
And use that in your <input>:
<input
name="Street Address"
value={warehouseData.name}
onChange={handleWarehouseDataChange}
/>

How to manage dynamic form data using useState?

I have a form that gets it's input fields dynamically, it can have hundreds of fields so i can't create a state individually, I was planning on doing something like using an object using the unique key of a form field but need some help.
Suppose the form has fields like this.
<form>
{inputFields.map((i) => {
<input type={i.type} />
})}
</form>
Now i would need a state like the one below
inputState = {
"INPUT_FIELD_NAME1": "INPUT FIELD VALUE 1",
"INPUT_FIELD_NAME2": "INPUT FIELD VALUE 2",
"INPUT_FIELD_NAME3": "INPUT FIELD VALUE 3",
}
I need help with this, how do i set values in such a manner in my input onChange and how do i access the values from the state and use them for the matching input field?
As per my understanding and knowledge, you have to update your dynamic structure like given as below
<form>
{inputFields.map((i) => (
<input
type={i.type}
name={`INPUT_FIELD_NAME${i.id}`}
onChange={handleChange}
/>
))}
</form>
Also have to update your react state on input change like
const [inputState, setinputState] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setinputState({
...inputState,
[name]: value
});
};
I hope it will work for you! Thanks.
I would do something like this:
const [form, setForm] = useState({})
const onChange = (event) => {
const { id, value } = event.target
setForm((prev) => ({ ...prev, [id]: value }))
}
<form>
{inputFields.map((i) => {
<input key={i.id} onChange={onChange} id={i.id} type={i.type} />
})}
</form>
Id must be unique, also you can use "name" property instead of "id"

How to update and display first non-empty value on setState with react?

I'm trying to display the value of my inputs from a from, in a list. Everytime I hit submit, I expect that it should display the inputs in order.
The problem I'm having is that when I try to submit my form and display inputs in a list, it display an empty value first. On the next submit and thereafter, it displays the previous value, not the new one on the input field.
There's also an error message but i'm not understanding how to relate it to the problem. It's a warning message regarding controlled/uncontrolled components.
I've tried to add if statements to check for empty values in each functions but the problem persists.I've tried to manage the error massage by being consistent with all input to be controlled elements using setState, but nothing works.
I looked through todo list examples on github. I guess i'm trying to keep it in one functional component versus multiple ones, and I'm not using class components. I tried to follow the wesbos tutorial on Javascript 30 day challenge, day 15: Local Storage and Event Delegation. I'm trying to use React instead of plain JS.
Here's what my component looks like.
import React, { useEffect, useState } from "react";
import "../styles/LocalStorage.css";
export const LocalStorage = () => {
const [collection, setCollection] = useState([]);
const [value, setValue] = useState();
const [item, setItem] = useState({ plate: "", done: false });
const [display, setDisplay] = useState(false);
//set the value of the input
const handleChange = (e) => {
if (e.target.value === "") return;
setValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
if (value === "" || undefined) return;
setItem((prevState) => {
return { ...prevState, plate: value };
});
addItem(item);
setDisplay(true);
setValue("");
};
const addItem = (input) => {
if (input.plate === "") return;
setCollection([...collection, input]);
};
return (
<div>
<div className="wrapper">
<h2>LOCAL TAPAS</h2>
<ul className="plates">
{display ? (
collection.map((item, i) => {
return (
<li key={i}>
<input
type="checkbox"
data-index={i}
id={`item${i}`}
checked={item.done}
onChange={() =>
item.done
? setItem((state) => ({ ...state, done: false }))
: setItem((state) => ({ ...state, done: true }))
}
/>
<label htmlFor={`item${i}`}>{item.plate}</label>
</li>
);
})
) : (
<li>Loading Tapas...</li>
)}
</ul>
<form className="add-items" onSubmit={handleSubmit}>
<input
type="text"
name="item"
placeholder="Item Name"
required
value={value}
onChange={handleChange}
/>
<button type="submit">+ Add Item</button>
</form>
</div>
</div>
);
};
Since the setState function is asynchronous, you cannot use the state value item right after you fire the setItem(...). To ensure you get the latest value for your addItem function:
setItem((prevState) => {
const newItem = { ...prevState, plate: value };
addItem(newItem); // Now, it's getting the updated value!
return newItem;
});
And regarding the controlled and uncontrolled components, you can read the docs about it here. To fix your problem, you can initialize the value state with an empty string:
const [value, setValue] = useState('');

setState only setting last input when using object as state

Im trying to create a form with React. This form uses a custom Input component I created various times. In the parent form Im trying to get a complete object with all names and all values of the form:
{inputName: value, inputName2: value2, inputName3: value3}
For this, I created a 'component updated' hook, that calls the function property onNewValue to send the new value to the parent (two way data binding):
useEffect(() => {
if (onNewValue) onNewValue({ name, value });
}, [value]);
The parent form receives the data in the handleInputChange function:
export default () => {
const [values, setValues] = useState({});
const handleInputChange = ({
name,
value
}: {
name: string;
value: string | number;
}): void => {
console.log("handleInputChange", { name, value }); // All elements are logged here successfully
setValues({ ...values, [name]: value });
};
return (
<>
<form>
<Input
name={"nombre"}
required={true}
label={"Nombre"}
maxLength={30}
onNewValue={handleInputChange}
/>
<Input
name={"apellidos"}
required={true}
label={"Apellidos"}
maxLength={60}
onNewValue={handleInputChange}
/>
<Input
name={"telefono"}
required={true}
label={"Teléfono"}
maxLength={15}
onNewValue={handleInputChange}
/>
<Input
name={"codigoPostal"}
required={true}
label={"Código Postal"}
maxLength={5}
onNewValue={handleInputChange}
type={"number"}
/>
</form>
State of values: {JSON.stringify(values)}
</>
);
};
This way all elements from all inputs should be set on init:
{"codigoPostal":"","telefono":"","apellidos":"","nombre":""}
But for some reason only the last one is being set:
{"codigoPostal":""}
You can find the bug here:
https://codesandbox.io/s/react-typescript-vx5py
Thanks!
The set state process in React is an asynchronous process. Therefore even if the function is called, values has not updated the previous state just yet.
To fix, this you can use the functional version of setState which returns the previous state as it's first argument.
setValues(values=>({ ...values, [name]: value }));
useState() doesn't merge the states unlike this.setState() in a class.
So better off separate the fields into individual states.
const [nombre, setNombre] = useState("")
const [apellidos, setApellidos] = useState("")
// and so on
UPDATE:
Given setValue() is async use previous state during init.
setValues((prevState) => ({ ...prevState, [name]: value }));
The updated and fixed code, look at:
https://codesandbox.io/s/react-typescript-mm7by
look at:
const handleInputChange = ({
name,
value
}: {
name: string;
value: string | number;
}): void => {
console.log("handleInputChange", { name, value });
setValues(prevState => ({ ...prevState, [name]: value }));
};
const [ list, setList ] = useState( [ ] );
correct:
setList ( ( list ) => [ ...list, value ] )
avoid use:
setList( [ ...list, value ] )

Input not changing state REACTJS

The input doesn't change it's text. It comes pre-filled from the database. For example, if it comes with the text: example, if I press for example the s key, it console logs examples but in the DOM in is still example. Here is the handle code:
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
console.log(event.target.name);
console.log(event.target.value);
};
and the input field:
<input
type="text"
className="form-control"
name="note"
id=" note"
value={this.state.insurance.note}
onChange={this.handleChange}
/>
EDIT (fixed the render problem, but I don't get the data that I want when I submit the form)
Here is the code for my form:
handleSubmit = event => {
event.preventDefault();
var state = this.state;
axios
.put(`${API_URL}/` + state.id + `/update`, {
tip: state.tip,
date_exp: state.date_exp,
date_notif: state.date_notif,
note: state.note
})
.then(response => {
console.log(response.data);
// window.location = "/view";
})
.catch(error => {
console.log(error);
});
}
and my button is a simple submit button:
<button className="btn btn-success" type="submit">Save</button>
imjared's answer is correct: your problem is in the handleChange function, where you wrongly update the state.
That function should be something like the following one:
handleChange = event => {
const insurance = Object.assign({}, this.state.insurance);
insurance[event.target.name] = event.target.value;
this.setState({insurance});
};
In the first line of the function, you create a deep copy of the current object insurance saved in the state. Then, you update the copied object, and finally update the state.
You're changing this.state.note based on the name property of your input so it makes sense that this.state.insurance.note wouldn't see any updates.
If you try console.logging this.state above your <input>, I bet you'll see what you're after.

Categories