I have an issue I could use some help with.
I have a form that allows users to upload images. After an image is uploaded, the function returns the image URL as a string, which I save using setState. This string is passed into a form field as a value.
✨✨THE ISSUE✨✨
The URL string is received and updated successfully with setState after image is uploaded. I can verify this with console.log in the following images. However! Although the form shows the value of imageUrl has changed, after the user clicks submit, the form submits the ORIGINAL value, which is set to null. How do I make it read the updated value of imageUrl?
Where imageUrl is initialized
class SubmitForm extends React.Component {
constructor(props) {
super(props);
this.state = {
imageUrl: null,
}
this.onInputchange = this.onInputchange.bind(this);
}
onInputchange(event) {
this.setState({
[event.target.name]: event.target.value
});
console.log("ONCHANGE CALLED")
}
Function that uploads image and does setState
openWidget = () => {
// create the widget
window.cloudinary.createUploadWidget(
{
cloudName: '[redacted for stack overflow]',
uploadPreset: '[redacted for stack overflow]',
},
(error, { event, info }) => {
if (event === 'success') {
this.setState({
imageUrl: info.secure_url,
})
}
},
).open(); // open up the widget after creation
};
Code that sets the value in the form field
render() {
const { imageUrl, imageAlt } = this.state;
let fRef = null;
console.log("FORM IS RENDERED")
console.log("Curent value of imageUrl is ", imageUrl)
return (
<Grid container>
...
<Button type="button" className="btn widget-btn" onClick={this.openWidget}>Upload Image</Button>
...
<TextField name='imageUrl' value={this.state.imageUrl} onChange={this.onInputchange}/>
...
...
</Grid>
);
Before an image is uploaded
After
the onChange doesnt seem to have been called? It would console log if it did.
Form submission
The field is required, and it throws an error because its reading in the ORIGINAL value of imageURL (which is null). How do I make it read the updated value of imageUrl?
Related
I'm trying to setup a form. It has Edit feature where on edit I call an API and get the data into state.
I'm struggling to display data in the form after api call. There's no problem utilizing the API or calling the redux functions. Problem is that my Form only displays last data in the redux state but not the updated data.
That's how I'm doing the stuff.
Calling API if isEdit===True at the same time Form is being displayed on component mount.
Updateding state after success as an object called customer
accessing the customer object like this
const { customer } = useSelector((state) => state.customers)
Lets say I have a input field where I want to display the email of customer.
I'm handling this think like that:
email: isEdit ? customer?.email : '', // At this point there is some problem
It loads the previous data that was stored in the state.customer but not the new one.
I believe my email field is rendering first and then doesn't updated the value when change happens in state.customer.
So how I can fix this? So that email value should be changed at the same time if state.customer got changed
Here is the full component. Still removed irrelevant part.
const CustomerNewEditForm = ({ isEdit, id, currentUser}) => {
const dispatch = useDispatch()
const navigate = useNavigate()
console.log('isEdit', isEdit, 'id', id, 'currentUser', currentUser)
// get sales reps
const { customer } = useSelector((state) => state.customers)
// const customer = () => {
// return isEdit ? useSelector((state) => state.customers?.customer) : null
// }
const { enqueueSnackbar } = useSnackbar()
const defaultValues = useMemo(
() => ({
email: isEdit ? customer?.email : '',
name: isEdit ? customer?.name : '',
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentUser]
)
const methods = useForm({
resolver: yupResolver(NewUserSchema),
defaultValues
})
const {
reset,
watch,
control,
setValue,
handleSubmit,
formState: { isSubmitting }
} = methods
const values = watch()
useEffect(() => {
if (isEdit === true) {
dispatch(getCustomerDetails(id))
console.log(customer)
}
if (isEdit && currentUser) {
reset(defaultValues)
}
if (!isEdit) {
reset(defaultValues)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentUser])
const onSubmit = async () => {
try {
await new Promise((resolve) => setTimeout(resolve, 500))
reset()
let body = {
email: values.email,
name: values.name,
}
console.log(body)
dispatch(createCustomer(body))
enqueueSnackbar(!isEdit ? 'Create success!' : 'Update success!')
// navigate(PATH_DASHBOARD.admin.root)
} catch (error) {
console.error(error)
}
}
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Grid item md={3}>
{' '}
<RHFTextField name="name" label="Customer Name" />
</Grid>
<Grid item md={3}>
{' '}
<RHFTextField name="email" label="Email Address" />
</Grid>
</FormProvider>
)
}
export default CustomerNewEditForm
Here in the component defaultValues carries the previous data from customer object if its True and renders the form with those values. but new data comes a miliseconds later but form renders first.
First of all try to console.log your customer data and make sure that it gets a fresh data on last render.
If it gets fresh data, try take a look at your Input component, it might set some initial data, so the input will be editable and controlled by some state.
Try to modify your input's state on redux store update in useEffect.
Currently that's all that I can suggest, update your post with code with your form and input, also post your console.log result, if my answer doesn't helped you.
If the problem would be not in form\input state and console.log wouldn't show you actual updated data in last render, then I will need to see your redux store code to resolve this issue.
Hope it helped
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 });
}
CODE:
import React, { Component } from 'react';
import classes from './Form.css';
class Form extends Component {
state = {
firstName: '',
phoneNo: '',
showForm: false
}
displayFormHandler = (e) => {
e.preventDefault();
const updatedName = e.target.value;
this.setState({
firstName: updatedName,
});
}
displayPhoneNoHandler = (e) => {
e.preventDefault();
const updatedPhoneNo = e.target.value;
this.setState({ phoneNo: updatedPhoneNo });
}
handleFormSubmit = (e) => {
e.preventDefault();
this.setState({
showForm: true
});
}
render() {
return (
<div className={classes.Form}>
<form onSubmit={this.handleFormSubmit}>
<label>Name:</label>
<input type="text"
className={classes.inputArea}
name="firstName"
placeholder="Name"
onChange={this.displayFormHandler}
value={this.state.firstName} />
<label>PhoneNo:</label>
<input type="text"
className={classes.inputArea}
placeholder="PhoneNo"
name="phoneNo"
onChange={this.displayPhoneNoHandler}
value={this.state.phoneNo} />
<button type="submit" className={classes.Button}
clicked={this.handleFormSubmit} >Submit</button>
<div className={classes.UserInfo}>
{this.state.showForm && <p>UserName: {this.state.firstName}</p>}
{this.state.showForm && <p>UserName: {this.state.phoneNo}</p>}
</div>
</form>
</div>
);
}
}
export default Form;
The code is mentioned above.
I created a form in React.JS. It echoes back the data from the form response; however, the data suddenly disappears after refreshing the page. I would like to find a suitable method that would allow me to store the user's response to the form and display it (even after the page has been refreshed).
To display form data even after the user has reloaded the page you could use the browsers local storage and at the start of each session just read the stored data.
If you want to display form data after page reload you need to store them somewhere best place would use browser local storage API. To clear form data after submit is easy just add your two input state values to setState method that you already have in your submit method.
Firstly we will try to get data from localStorage in constructor of the component class. If localStorage is not empty we set its value as default component state. Here is a code example.
constructor() {
const data = JSON.parse(localStorage.getItem('formData')); // get data from localStorage and parse it.
if (data !== null) { // Check if data even exists
this.state = {
firstName: data.firstName,
phoneNo: data.phoneNo,
showForm: false
}; // set default React state.
}
}
Secondly set the data after form submit. Here is another code example.
handleFormSubmit = (e) => {
e.preventDefault();
const {firstName, phoneNo} = this.state;
localStorage.setItem('formData', JSON.stringify({ firstName, phoneNo })); // parse the data and save it.
this.setState({
firstName: '',
phoneNo: '',
showForm: true
}); // Clear the form data
}
I'm making a mobile app in React-Native where the user must login first to use the functionalities. If the user logs in succesfully, I save the username with AsyncStorage.setItem, which works great.
I try to get the value on app start using AsyncStorage.getItem, using the code:
class LoginScreen extends Component {
static PropTypes = {
dispatch: PropTypes.func,
fetching: PropTypes.bool,
attemptLogin: PropTypes.func
}
isAttempting = false
constructor (props) {
super(props)
this.state = {
username: '',
password: '',
isLoaded: {false}
}
}
componentDidMount(){
const { username, password } = this.state
this._getSavedUsername();
// AsyncStorage
// .getItem('#MobAppCorpStore:username')
// .then((v_username) => { this.setState({ username: v_username, isLoaded: true }) });
}
async _getSavedUsername() {
const { username } = this.state
try {
const value = await AsyncStorage.getItem('#MobAppCorpStore:username');
if (value !== null){
this.setState({ username: value});
}
} catch (error) {
// Error retrieving data
alert('Containers/LoginScreen - _getSavedUsername error')
console.log('Containers/LoginScreen - get username error : ', error)
}
}
...
render () {
...
<Form style={styles.form_style}>
<Item floatingLabel >
<Label>{I18n.t('Global.username')}</Label>
<Input
style={styles.input_style}
//autoFocus = {true}
returnKeyType='next'
value={username}
onChangeText={username => this.setState({username})}
autoCapitalize="none"
blurOnSubmit={false}
/>
</Item>
<Item floatingLabel last >
<Label>{I18n.t('Global.password')}</Label>
<Input
style={styles.input_style}
returnKeyType='go'
secureTextEntry={true}
value={password}
onChangeText={password => this.setState({password})}
onSubmitEditing={this._handlePressLogin}
autoCapitalize="none"/>
</Item>
</Form>
...
}
...
}
The value is correctly recovered and displayed in the TextInput (Input from NativeBase).
But I also get this error:
Warning: Can't call setState (or forceUpdate) on an unmounted component.
This is a no-op, but it indicates a memory leak in your application. To fix,
cancel all subscriptions and asynchronous tasks in the componentWillUnmount
method.
If I understood correctly, AsyncStorage is an async function, so the value is recovered "later", after the lifecycle in this case?
What is the correct way to get a value using AsyncStorage at the start of an app/screen?
(I've already seen this post What is the correct way to use AsyncStorage to update state in React-Native? but I think it's not similar to my case)
The problem you are experiencing is probably caused by you are trying to send parameter inside get item username. If you want to save your keys as '#MobAppCorpStore:username' syntax you should use something like this #MobAppCorpStore:${username}
let name = "John"
let point = "100"
try {
await AsyncStorage.setItem(`#MobAppCorpStore:${name}`, point);
} catch (error) {
// Error saving data
}
And by using your key you can get the value of latest
try {
const grade = await AsyncStorage.getItem(`#MobAppCorpStore:${name}`);
if (value !== null){
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
}
I have a simple form allowing users to create a plain text post. The code below generates a successful server side response following the createPostRequest call. However, following a successful post, I would like to update the state to empty out the postBody field and update the UI to reflect this change, and then allow users to make subsequent requests to post additional messages.
Currently, everything works well for the first request only, and after a successful initial request, the postBody field isn't emptying out and when attempting to change the value of the post field following the first initial request, every key stroke is resulting in the following error:
Uncaught TypeError: Cannot read property 'setState' of undefined
Note, what is a bit odd, is that I am getting the above error despite binding this to the onChange method in the constructor.
Did anyone run into this issue? I appreciate any suggestions on how to resolve.
constructor(props) {
super(props);
this.state = {
postBody : "",
location: "main"
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
onChange(e) {
this.setState({
[e.target.name] : e.target.value
});
}
onSubmit(e) {
e.preventDefault();
this.props.createPostRequest(this.state).then(
() => {
this.setState = ({
postBody : ""
});
}
)
}
render() {
return (
<div className="create-post-inner col-md-12">
<form id="createPost" onSubmit={ this.onSubmit } >
<textarea value={this.state.postBody} className="form-control postInput" name="postBody" onChange={ this.onChange } >
</textarea>
<span>The value of the state is {this.state.postBody}</span>
<input type="submit" className="submit btn btn-primary" />
</form>
</div>
);
}
there is an extra = after this.setState . Change the following to
this.props.createPostRequest(this.state).then(
() => {
this.setState({
postBody : ""
});
}
)