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.
Related
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}
/>
I have created a basic form in react.js where I am able to get the values after the user submits the form.
However, when I try to change the values using the handleSubmit function, I don't see the changes made in the state.
I have made a copy of a state and changes are being reflected in the Copied State. But when I set the old state equal to the updated state, the changes are not reflected
My code is as follows
state = {
name: null,
ContactNumber: null
}
handleChange = (event) => {
this.setState({
[event.target.name] : event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
let Copystate = JSON.parse(JSON.stringify(this.state))
Copystate.ContactNumber = 100
console.log(Copystate) // displaying the contact number as 100
this.setState({
state : Copystate
})
console.log(this.state) // displays the number which was submitted in the form
}
render(){
return(
<div>
<h2>Form</h2>
<form onSubmit={this.handleSubmit}>
<div>
<label>Name</label>
<input type="text" name="name" required = {true} onChange = {this.handleChange}/>
<label>Contact Number</label>
<input type="number" name="ContactNumber" required = {true} onChange = {this.handleChange}/>
<button type="submit" label="submit" >Submit</button>
</div>
</form>
</div>
);
}
}
Can anyone please let me know where I am going wrong? Thanks
Notice: setState is asynchronous: document state-updates-may-be-asynchronous
You can use a callback function to get the updated state
this.setState({state: Copystate}, () => {console.log(this.state)});
Or you can choose to use async/await
handleSubmit = async (event) => {
await this.setState({state: Copystate});
console.log(this.state);
}
Those two methods won't affect re-render since once the state is been updated, the re-render would proceed.
If you console in the render() you would find that it should always be updated finally.
render() {
console.log(this.state);
return (
...
)
}
setState is asynchronous.
So, you can do one of the following -
1. make a callback in setState to log the state or
2. write your console statement in the render function.
Why do you do this?
let Copystate = JSON.parse(JSON.stringify(this.state))
Copystate.ContactNumber = 100
You can change the handleSubmit to be like the following:
handleSubmit = (event) => {
event.preventDefault();
let { ContactNumber } = this.state;
ContactNumber = 100;
console.log(ContactNumber); // displaying the contact number as 100
this.setState({
ContactNumber: ContactNumber
}, () => {
console.log(this.state) // displays the number which was submitted in the form
})
}
I have a list of input to generate dynamically from an array of data I retrieve from an API.
I use .map() on the array to generate each of my input, and set value={this.state.items[i]} and the onChange property (with a modified handleChange to handle change on an array properly).
Now, I set in my constructor this.state = { items: [{}] }, but since I don't know how many items are going to be generate, value={this.state.items[i].value} crash since this.state.items[n] doesn't exist.
The solution is then to set each this.state.items[i] = {} (using Array.push for example) first, and then generate all the inputs.
var apiData = [{ value: "" }, { value: "" }]
this.setState({
items: apiData,
inputs: apiData.map((v, i) => {
return <input key={i} value={this.state.items[i].value}
onChange={(e) => this.handleChangeArray(e, i)} />
})
})
https://jsfiddle.net/qzb17dut/38/
The issue with this approach is that this.state.items doesn't exist yet on value={this.state.items[i].value} and we get the error Cannot read property 'value' of undefined.
Thankfully, setState() comes with a handy second argument that allows to do something only once the state is set. So I tried this:
var apiData = [{ value: "" }, { value: "" }]
this.setState({
items: apiData,
}, () => this.setState({
inputs: apiData.map((v, i) => {
return <input key={i} value={this.state.items[i].value}
onChange={(e) => this.handleChangeArray(e, i)} />
})
}))
https://jsfiddle.net/qzb17dut/39/
(Update: Please have a look at this example that better illustrate the use case: https://jsfiddle.net/jw81uo4y/1/)
Looks like everything should work now right? Well, for some reason, I am having this very weird bug were value= doesn't update anymore like when you forget to set onChange= on an input, but here onChange= is still called, value= is just not updated making the field remaining not editable.
You can see on the jsfiddle the problem for each method. The first one doesn't have the state set yet, which would allow the input to be edited, but crash because the state value was not yet set. And the second method fix the first issue but introduce this new weird bug.
Any idea about what I am doing wrong? Am I hitting the limit of react here? And do you have a better architecture for this use case? Thanks!
What about this approach instead, where you set the state of the API values only and then, generate the input based on the state from the render via Array.prototype.map like so
constructor (props) {
this.state = {items: []}
}
async componentDidMount(){
const apiData = await fetchApiData()
this.setState({items: apiData})
}
handleChange = (value, index) => {
const items = this.state.items;
items[index].value = value;
this.setState({ items });
};
updateState = () => {
const items = this.state.items;
items.push({value: ''}); // default entry on our item
this.setState({ items });
};
// here ur state items is exactly same structure as ur apiData
onSubmit =()=> {
console.log('this is apiData now', this.state.items)
}
render () {
<button onClick={this.updateState}>update state with inputs</button>
<button onClick={this.onSubmit}>Submit</button>
{this.state.items.map((item, index) => (
<input
key={index}
value={item.value}
onChange={e => this.handleChange(e.target.value, index)}
/>
))}
}
here is the codesandbox code for it
https://codesandbox.io/s/icy-forest-t942o?fontsize=14
with this, it will generate the input based on the items on the state, which in turns have the click handler which updates the state.
Well if I understand correctly, apiData is assigned to state.items and then also used to generate the inputs array. That means that for your purpose apiData and state.items are equivalent. Why don't you use the third map argument like:
var apiData = [{ value: "" }, { value: "" }]
this.setState({
items: apiData,
inputs: apiData.map((v, i, arr) => {
return <input key={i} value={arr[i].value}
onChange={(e) => this.handleChangeArray(e, i)} />
})
});
or the apiData array directly?
I am trying out with a small react app, with a form based data capture, after keying in the values, when user clicks submit button, values need to be captured in state variable. But, state variable contains null value in handleSubmit function. For clarity, code snippets are given below,
Constructor code, for the sake of completeness/clarity,
constructor(props)
{
super(props);
this.state = {
username : '',
};
}
handleChange function is where I set state variable,
handleChange = (event) => {
this.setState( [event.target.username]: event.target.value );
}
handleSubmit function is where, I print state variable, which contains null value, instead of user inputted value.
handleSubmit = () => {
console.log(this.state.username);
}
Component's render function is given below, which invokes handleChange
and handleSubmit.
render() {
return(
<div>
<form>
<label>
Title:
<input
type = "text"
name="username"
onChange={event => this.handleChange(event)}/>
</label>
<button
label="Submit"
onClick={this.handleSubmit}>
Submit
</button>
</form>
</div>
)};
I am missing on something. I am new to react. Kindly advise.
you need setState's argument to be an object.
handleChange = (event) => {
this.setState({ [event.target.username]: event.target.value });
}
Given the source code for a simple login form, see below. You see I want to use the username text field's value when I click the form's submit button. Since I need a reference to the actual DOM node to retrieve the value, I'm setting the usernameElement variable to that node.
const Login = ({ onLogin }) => {
let usernameElement
const handleSubmit = event => {
event.preventDefault()
onLogin(usernameElement.value)
}
return <form onSubmit={handleSubmit}>
<input
type="text"
name="username"
ref={node => { usernameElement = node }}
/>
<button type="submit">Login</button>
</form>
}
So, how would I make an functional approach to that problem, or simply get rid of the let variable?
Apparently, the correct approach to this is to make use of the component's state, meaning you need a stateful component instead of the stateless one.
// untested
class Login extends Component {
state = { username: '' }
handleChange = event => {
this.setState({ username: event.target.value })
}
handleSubmit = event => {
event.preventDefault()
this.props.onLogin(this.state.username)
}
render = () =>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="username"
value={this.state.username}
onChange={this.handleChange}
/>
<button type="submit">Login</button>
</form>
}
Using this, the username is always bound to this.state.username. The handleSubmit method can just use the username from the component's state.
See this page for further information: https://facebook.github.io/react/docs/forms.html