Pass default value to useState in React - reactjs

I am having a form that passes values to a useState
I have this showrefid its value is set to either true or false
I want to pass the value of inputs into setAllValues based on true or false of showrefid
if showrefid is false I want to pass a default value that is already prefixed as value sony
{showrefid ? (
<input
name="userid"
onChange={handleChange}
style={styles.input}
type="text"
placeholder="userid"
/>
) : (
<input
name="userid"
value="sony"
onChange={handleChange}
type="text"
readonly="readonly"
/>
)}
const handleChange = (e) => {
setAllValues({ ...allValues, [e.target.name]: e.target.value });
};

You can set a default value to useState whenever the component gets rendered for the first time
const [allValues, setAllValues] = useState(showrefid ? { userid: "sony" } : {})
After that, you can set it directly to the input field
<input
name="userid"
value={allValues.userid}
onChange={handleChange}
type="text"
readonly="readonly"
/>
If showrefid can be modified from other places and you want to update your state accordingly, you can call useEffect
useEffect(() => {
if(!showrefid) {
setAllValues({ ...allValues, userid: "sony" })
}
}, [showrefid])

maybe consider using showrefid variable inside your handleChange function?
const handleChange = (e) => {
setAllValues(showrefid
? { ...allValues, [e.target.name]: e.target.value }
: { /* whatever value you want to set when false */ });
};

Related

Formik use debounce on change input

I have this simple example using useDebouncedCallback from use-debounce. When i write to input it remains the same with no value. What iam doing wrong?
const SignupForm = () => {
const formik = useFormik({
initialValues: {
firstName: "",
},
});
const debounced = useDebouncedCallback(
// function
(event) => {
formik.handleChange(event);
},
// delay in ms
1000
);
return (
<form onSubmit={formik.handleSubmit}>
<label htmlFor="firstName">First Name</label>
<input
id="firstName"
name="firstName"
type="text"
onBlur={formik.handleBlur}
onChange={(e) => {
e.persist();
debounced(e);
}}
value={formik.values.firstName}
/>
<button type="submit">Submit</button>
</form>
);
};
It's not working because you have a controlled component (due to value={formik.values.firstName}).
When formik.handleChange is called (in an debounced way), e.target.value is empty because React keeps the input field in sync with formik.values.firstName which remains empty (its initial value is firstName: "").
To make it work, you can use the formik setFieldValue and pass the input name and value to the debounced function like this :
const debounced = useDebouncedCallback(
(field, value) => formik.setFieldValue(field, value),
1000
);
...
<input
id="firstName"
name="firstName"
type="text"
onBlur={formik.handleBlur}
onChange={(e) => {
const { name, value } = e.target;
debounced(name, value);
}}
value={formik.values.firstName}
/>
Here is a stackblitz example

Formik - How do I control the value of another input field automatically when one field changes?

I'm using Formik's useFormik hook to manage my forms in ReactJS. But I don't know how to build a custom onChange handler that works on top of or alongside Formik's handleChange function. I tried building a custom change handler like below but I'm unable to figure out what step to take next.
import React from "react";
import { useFormik } from "formik";
function testForm() {
const { handleChange, values } = useFormik({
initialValues: { title: ``, slug: `` },
onSubmit: ( values ) => console.log( values )
})
const customHandleChange = e => {
// let's suppose title field changed.
const { value } = e.target;
// I can do other functions here, like fetching data from API
store.dispatch( getUserByTitle( value ))
// But I'm trying to set a custom value for the slug field automatically depending on changing title field value
let slugValue = value.toLowerCase()
// handleChange function changes the value of title input. How do I also change slug input?
handleChange(e)
}
return (
<div>
<input type="text"
name="Title"
value={ values.title }
onChange={ customHandleChange }/>
<input type="text"
name="slug"
value={ values.slug }
onChange={ handleChange } />
</div>
)
}
I tried setting value of the slug input directly as follows.
<input type="text"
name="slug"
value={ values.title.toLowerCase() }
onChange={ handleChange } />
But if I do that, I won't be able to type in the field and make a change as the value is strictly set from the title input. Users should also be able to change the slug field value to whatever they want.
Try using setFieldValue:
function testForm() {
const { handleChange, values, setFieldValue } = useFormik({
initialValues: { title: ``, slug: `` },
onSubmit: ( values ) => console.log( values )
})
const customHandleChange = e => {
const { value } = e.target;
setFieldValue('title', value);
setFieldValue('slug', value.toLowerCase());
}
return (
<div>
<input type="text"
name="Title"
value={ values.title }
onChange={ customHandleChange }/>
<input type="text"
name="slug"
value={ values.slug }
onChange={ handleChange } />
</div>
)
}

Adding an arrow function to input field results an error

Here is a React class I'm working on:
import React from 'react';
export default class ExpenseForm extends React.Component {
state = {
title: ''
};
onTitleChange = (e) => {
const title = e.target.value;
this.setState(() => ({title}));
};
render() {
return (
<div>
<form>
<input
type='text'
placeholder='title'
value={this.state.title}
onChange={(e) => this.setState(() => ({title: e.target.value}))}
required autoFocus/>
<textarea placeholder='Add a note for your expense'/>
<input type='number' placeholder='amount' required/>
<input type='submit' value='Add Expense'/>
</form>
</div>
);
}
}
This throws an error Uncaught TypeError: Cannot read property 'value' of null when executing onChange.
But when I restructure inner js of onChange into a separate function onTitleChange and calling that function: onChange={this.onTitleChange}, it works perfectly. What could be the reason behind this behavior?
Here you assigned onchange event as onChange={(e) => this.setState(() => ({title: e.target.value}))}, here e.target.value will not work, because its inside the setState() scope.
On expanding your function, we will get
function(e){
this.setState(function(){
return {
title: e.target.value
}
})
}
Here there is no e in function inside setSate(), so e.target.value will be error;
Since you dont want to compare with previous value and only need to set the value to title, you can use like
onChange={(e) => this.setState({title: e.target.value})}
There is no need for an extra function inside setState
I would guess, that your parameter e is not known in the inner arrow function.
You could write it like this:
<input
type='text'
placeholder='title'
value={this.state.title}
onChange={e => this.setState({ title: e.target.value })}
required
autoFocus
/>
That is because of React is utilizing event pooling and setState is a function executed in asynchronous context, so you observe them as nulls in the asynchronous setState callback.
You need to either persist event using e.persist() or save the value of event to variable, as in your method.
You don't need to use arrow function inside setstate.
change
onChange={(e) => this.setState(() => ({title: e.target.value}))}
to:
onChange={(e) => this.setState({title: e.target.value})}

React js - Use only one state instead of multiple state to handle "event.target.value"

I'm new in React js. I created an app that have input fields and when you click the submit button the value of the input fields will display in console.log depends on the value entered in input fields. The question is can i use only one state to display the following that I entered in the input fields?
this is my code:
constructor () {
super ();
this.state = {
username: '',
password: '',
place: '',
birthPlace: '',
}
}
username = () => {
this.setState({ username: event.target.value })
}
password = () => {
this.setState({ password: event.target.value })
}
place = () => {
this.setState({ place: event.target.value })
}
birthPlace = () => {
this.setState({ birthPlace: event.target.value })
}
What I want to happen is use only one state for username, password, place, birthPlace (if possible) and what I want to happen is when I click the submit button, the value of which I've type in the Input fields should display in console.log
Just for optimization purposes. Thanks for the help
You can use the dictionary objects.
Define your state like this
constructor () {
super ();
this.state = {
formvalues:{}
}
}
and you can update your values by defining any key you want.
this.setState(prevState => ({
formvalues: {
...prevState.formvalues,
[username]: value
}
}))
and you can access any input field based on the key you defines.
`console.log(this.state.formvalues.username)`
constructor () {
super ();
this.state = {
username: '',
password: '',
place: '',
birthPlace: '',
}
}
You can use one method for all values like while using the same state:
handleChange = event => {
const { value, name } = event.target;
this.setState({ [name]: value });
};
Its probably better to use hooks for this, something like the code below would do it
import React , {useState} from 'react'
const myComp = () =>{
const [userDetails, setUserDetails] = useState({})
handleOnChange({target:{value, id}}) =>{
let newState = Object.assign({}, userDetails, {[id]: value})
setUserDetails(newState)
}
return (
<form>
<input type='text' id='name' value={userDetails['name']} onChange={handleOnChange} />
<input type='password' id='password' value={userDetails['password']} onChange={handleOnChange} />
<input type='email' id='email' value={userDetails['email']} onChange={handleOnChange} />
</form>
)
}
You can try this example that uses React hooks:
function App() {
const [state, setState] = React.useState({
username: '',
password: '',
place: '',
birthPlace: '',
});
function handleChange(event) {
const { name, value } = event.target;
console.log(name);
console.log(value);
setState(prevState => ({
...prevState,
[name]: value
}));
}
function handleSubmit(event) {
console.log(state);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>UserName</label>
<input type="text" name="username" value={state.username} onChange={handleChange} /><br />
<label>Password</label>
<input type="password" name="password" value={state.password} onChange={handleChange} /><br />
<label>Place</label>
<input type="text" name="place" value={state.place} onChange={handleChange} /><br />
<label>BirthPlace</label>
<input type="text" name="birthPlace" value={state.birthPlace} onChange={handleChange} /><br />
<input type="submit" value="Submit" />
</form>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
.as-console-wrapper {
height: 80px !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
More info: https://reactjs.org/docs/forms.html
Be aware that setting the state directly with the event.target.value will not always produce the expecting result.
React uses the Synthetic event wrapper and to be able to update the state directly with the event.target.value it uses persist() to make the event persist long enough to be used in the setState() method.
What I suggest doing to use event.target.value in class components is to store the value and then update the state.
So instead of doing this:
username = (event) => {
this.setState({ username: event.target.value })
}
You do this:
username = (event) => {
const value = event.target.value;
this.setState({ username: value })
}
This way the value is preserved long after the event has been fired.
You will notice that I added the event as parameter in the above example. This is to answer your second part of the question regarding on how to update the state. You need to constantly call the username method each time the typed username is changed. You can do this by calling it via the onChange listener:
<input name='username' onChange = {(event) => this.username(event)} />

emptying the input after submiting a form

I'm trying to empty the input tag once I'm updating my state:
state = {
formName: '',
inputs: [],
tempInput: {
inputLabel: '',
inputType: '',
inputValue: ''
}
};
this is my form:
<div className="formG">
<form className="form-maker" onSubmit={this.handleSubmit}>
Label:
<input name="inputLabel" type="text" onChange={this.handleChange} />
Type:
<input name="inputType" type="text" onChange={this.handleChange} />
Value
<input name="inputValue" type="text" onChange={this.handleChange} />
Form Name
<input name="formName" type="text" onChange={this.formName} />
<button>Submit</button>
</form>
that's how I handle the change
handleChange = e => {
const { name, value } = e.target;
this.setState(currentState => ({
tempInput: { ...currentState.tempInput, [name]: value }
}));
};
and I tried to just empty the tempInput but it doesn't work, anybody knows why?
handleSubmit = e => {
e.preventDefault();
const inputs = [...this.state.inputs, this.state.tempInput];
const { tempInput } = this.state;
tempInput.inputLabel = '';
tempInput.inputType = '';
tempInput.inputValue = '';
this.setState({ inputs, tempInput });
};
Your form is an uncontrolled component, so they are not controlled by the state fields. That's why your approach didn't work. Instead you can do e.target.reset() which will clear the entire form. But if you want to reset some input, you can access them and set the .value to "" as I had shown below.
An uncontrolled component works like form elements do outside of React. When a user inputs data into a form field (an input box, dropdown, etc) the updated information is reflected without React needing to do anything. However, this also means that you can’t force the field to have a certain value. From Doc
So your handleSubmit method will look like:
handleSubmit = e => {
e.preventDefault();
const inputs = [...this.state.inputs, this.state.tempInput];
// ....
// The below will reset entire form.
// e.target.reset();
// If you want some of them to empty.
const { elements } = e.target
elements['inputLabel'].value = "";
elements['inputType'].value = "";
elements['inputValue'].value = "";
};
Check the doc of HTMLFormElement.elements
Your input tags are not displaying the value of your state.
1) pull the individual values out of tempInput
2) use the value stored in your state that is then updated by your handleChange.
3) In your handleSubmit function reset your individual values to and empty string.
your handleChange should look like:
handleChange = e => {
const { name, value } = e.target;
this.setState([name]: value);
};
your jsx should look like :
<form className="form-maker" onSubmit={this.handleSubmit}>
Label:
<input name="inputLabel" value={this.state.inputLabel} type="text" onChange={this.handleChange} />
Type:
<input name="inputType" value={this.state.inputType} type="text" onChange={this.handleChange} />
Value
<input name="inputValue" value={this.state.inputType} type="text" onChange={this.handleChange} />
Form Name
<input name="formName" value={this.state.formName} type="text" onChange={this.formName} />
<button>Submit</button>
</form>
You're mutating the original state. You can copy and then only set the state. Just changing the following will work fine for you.
Replace this:
const { tempInput } = this.state;
With this:
const { tempInput } = {...this.state}; // copy the state
Also, be sure to bind the state value in your input elements like this to make them controlled component:
<input name="inputLabel" type="text"
onChange={this.handleChange}
value={this.state.tempInput.inputLabel || ''} />
And your handler should be:
handleChange = e => {
const { value } = e.target;
this.setState({value});
// now, value will correspond to the controlled component
};
Also take care react suggest to use controlled component as far as possible:
In most cases, we recommend using controlled components to implement forms.

Resources