React - Date input value not updated to state - reactjs

In my React App, I am able to set the state and update the database for all values except the date input field. My code is below:
import React, { Component } from 'react'
...
...
import DateInput from '../others/input/datePicker'
...
..
change = (what, e) => this.setState({ [what]: e.target.value })
changeDOB() {
this.setState({ age: document.getElementByClassNames("datePicker").value })
}
render() {
let {
...
...
age,
...
} = this.state
...
...
//date of birth
let stringAge = age.toString()
stringAge =
stringAge.substring(0, 4) +
'-' +
stringAge.substring(4, 6) +
'-' +
stringAge.substring(6, 8)
...
<DateInput
type="date"
change={this.changeDOB}
placeholder={stringAge}
className="datePicker"
/>
...
...
const mapStateToProps = store => ({
ud: store.User.user_details,
tags: store.User.tags,
session: store.User.session,
})
export default connect(mapStateToProps)(EditProfile)
export { EditProfile as PureEditProfile }
Here is DateInput code:
import React from 'react'
import { string, func, oneOf, bool } from 'prop-types'
const DateInput = ({ type, placeholder, ...props }) => (
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
{...props}
/>
)
DateInput.defaultProps = {
type: 'date',
placeholder: '',
disabled: false,
}
DateInput.propTypes = {
type: oneOf(['date']),
placeholder: string.isRequired,
maxLength: string,
disabled: bool,
}
export default DateInput
I tried this.change like other fields but that does not work either.
How to get the new value updated in the state ?
Note: The text is red is the value currently in the database.

You need to add onChange attribute for the input field in the DateInput component as
const DateInput = ({ type, placeholder, ...props }) => (
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
onChange = {props.Onchange}
{...props}
/>
)
Then your main component should be as
changeDOB(e) {
this.setState({ age: e.target.value });
}
render() {
return(
<DateInput
type="date"
Onchange={this.changeDOB}
placeholder={stringAge}
className="datePicker"
/>
)
}
Please find a working example here

You are passing all the props to input component but you need to pass your event handler function to onchange input element or Try onkeypress instead. Something like below. You can also try getting input value with event instead of document
Arrow function: No need of manual binding
changeDOB = (event) => {
this.setState({ age: event.target.value
})
}
<DateInput
type="date"
change={this.changeDOB}
placeholder={stringAge}
className="datePicker"
value={this.state.age}
/>
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
onchange={(event) => props.change(event)}
value={props.value}
{...props}
/>
Normal function: Binding required and only in constructor
this.changeDOB = this.changeDOB.bind(this);
changeDOB(event){
this.setState({ age: event.target.value
})
}
<DateInput
type="date"
change={this.changeDOB}
placeholder={stringAge}
className="datePicker"
value={this.state.age}
/>
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
onchange={props.change}
value={props.value}
{...props}
/>

The date is being taken in the ISO format whereas the display expects it in the localformat.
This worked for me:
const [deadline, setDeadline] = useState(new Date());
<input
type="date"
id="start"
name="trip-start"
value={deadline.toLocaleDateString}
onChange={(event) => setDeadline({deadline:event.target.value})}
min="2022-01-01"
max="2022-12-31"
>
</input>

Related

How to make input field text editable?

I have a problem of trying to make the input text field editable.
Currently, I am unable to edit the values of the input text field where i can remove or add new characters to the value in the input text field.
I have set the values statically in the state objects but I also want to edit the state values from the input text field.
How can I edit the code below to make the value editable?
import React, { Component } from 'react';
import { render } from 'react-dom';
class Info extends Component {
constructor(props) {
super(props);
this.state = {
name: "Jack Sparrow",
age: "52",
email: "jacksparrow52#gmail.com"
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
let newState = {...this.state};
newState[e.target.name] = e.target.name
this.setState({
...newState
})
}
render() {
return (
<div>
<input type="text" name="name" value={this.state.name} placeholder="Enter your name..." onChange={(e) => this.handleChange(e)} />
<br /> <br />
<input type="text" name="age" value={this.state.age} placeholder="Enter your age..." onChange={(e) => this.handleChange(e)} />
<br /> <br />
<input type="text" name="email" value={this.state.email} placeholder="Enter your email..." onChange={(e) => this.handleChange(e)} />
<h3>Output states:</h3>
<p id="name">Entered Name: {this.state.name}</p>
<p id="age">Entered Age: {this.state.age}</p>
<p id="email">Entered Email: {this.state.email}</p>
</div>
);
}
}
render(<Info />, document.getElementById('root'));
You are setting the state to the target input name. Fix this line
newState[e.target.name] = e.target.name
with (notice e.target.value)
newState[e.target.name] = e.target.value
Change your input field like this, Add input field name and bind handle Change inside the input field. Now you do not want to bind handle Change in constructor.
<input type="text" name="name" value={this.state.name} placeholder="Enter your name..." onChange={this.handelChange.bind(this, 'name')} />
Now replace handle change function,
handelChange(field, event) {
this.setState({
[field]: event.target.value
})
}
I hope this helps, I created a codesandbox with this solution
https://codesandbox.io/s/editable-inputs-m4fqk6?file=/src/App.tsx:256-2201
import { useState } from "react";
interface infoProfile {
name: string;
email: string;
}
const App = () => {
const [editCancel, setEditCancel] = useState(false);
const [value, setValue] = useState<infoProfile>({
name: "Anakin Skywalker",
email: "anakin#empire.com"
});
const onClick = (): void => {
setValue({ email: value.email, name: value.name });
setEditCancel(false);
};
return (
<>
<h1>Editable Inputs</h1>
<div>
<button onClick={() => setEditCancel(!editCancel)}>
{editCancel ? "Cancel" : "Edit"}
</button>
{editCancel && <button onClick={onClick}>Save</button>}
</div>
{!editCancel && (
<div>
<h2>Name</h2>
<div>{value.name}</div>
<h2>Email</h2>
<div>{value.email}</div>
</div>
)}
{editCancel && (
<div>
<h2>Name</h2>
<input
value={value.name}
onChange={(e) =>
setValue({ name: e.target.value, email: value.email })
}
/>
<h2>Email</h2>
<input
value={value.email}
onChange={(e) =>
setValue({ email: e.target.value, name: value.name })
}
/>
</div>
)}
</>
);
};
export default App;

React phone input 2 with react hook form

I am using PhoneInput along with react hook form, I want to enable save button only if phone number is valid
Code:
<form onSubmit={handleSubmit(onSubmitRequest)}>
.....................other code..............
<Controller
as={
<PhoneInput
id="pNum"
placeholder="Enter phone number"
className={classes.phoneInput}
inputRef={register({required: true})}
isValid={(inputNumber, onlyCountries) => {
return onlyCountries.some((country) => {
return startsWith(inputNumber, country.dialCode) || startsWith(country.dialCode, inputNumber);
});
}}
/>
}
name="phoneNumber"
control={control}
/>
........................other code...................
<Button
fullWidth
type="submit"
variant="contained"
color={'primary'}
className={classes.submitBtn}
data-testid="customerFormButton"
disabled={!formState.isValid}
>
Save
</Button>
</form>
Here I used PhoneInput as controller along with isValid for it. How can I disable Save button for invalid phone number input?
How are you? I believe that your problem is because you are not configuring the rules for the controller.
You need to change your controller to something like this:
<Controller
as={
<PhoneInput
id="pNum"
placeholder="Enter phone number"
className={classes.phoneInput}
inputRef={register}
isValid={(inputNumber, onlyCountries) => {
return onlyCountries.some((country) => {
return startsWith(inputNumber, country.dialCode) || startsWith(country.dialCode, inputNumber);
});
}}
/>
}
name="phoneNumber"
control={control}
rules= {{required: true}}
/>
ref cannot be currently used on this element. react-phone-input-2.
Until its supported, you can provide a hidden input field which updates its value when the phone updates its value and put the ref on that
Example:
import React, { FC, useCallback } from 'react';
import { useFormContext } from 'react-hook-form';
import PhoneInput from 'react-phone-input-2';
import 'react-phone-input-2/lib/style.css';
interface Props {
handleChange: (name: string, val: string) => void;
defaultValue: string;
name: string;
}
const MyComponent: FC<Props> = ({ defaultValue, name, handleChange }) => {
const { register, setValue, watch } = useFormContext(); // Note: needs <FormProvider> in parent for this to be acessible
const nameHidden = `${name}Hidden`;
const handleChangePhone = useCallback(
(val: string) => {
setValue(nameHidden, val, { shouldValidate: true });
handleChange(name, val);
},
[handleChange]
);
return (
<>
<PhoneInput value={defaultValue as string} country="gb" onChange={handleChangePhone} />
<input
type="hidden"
name={nameHidden}
defaultValue={defaultValue}
ref={register({
// validate stuff here...
})}
/>
</>
);
};
export default MyComponent;

React Native onChangeText like onChange in ReactJS

I learn in the same time ReactJS and React Native. I saw in an udemy tutorial something very beautiful, that the professor putt it just one onChange method, for all inputs and taking advantage of the "name" attribute, he could do this:
const onChange = event =>
setFormData({ ...formData, [event.target.name]: event.target.value });
So he said, instead of having for each onChange, inside of each input, a different method, we can have only one.
This is the code that I'm talking about:
const Register = props => {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
password2: ''
});
const { name, email, password, password2 } = formData;
const onChange = event =>
setFormData({ ...formData, [event.target.name]: event.target.value });
const onSubmit = async event => {
event.preventDefault();
if (password !== password2) {
props.setAlert('Passwords do not match', 'danger', 5000);
} else {
props.registerUser({ name, email, password });
}
};
if (props.isAuthenticated) {
return <Redirect to="/dashboard" />;
}
return (
<Fragment>
<h1 className="large text-primary">Sign Up</h1>
<p className="lead">
<i className="fas fa-user" /> Create Your Account
</p>
<form className="form" onSubmit={event => onSubmit(event)}>
<div className="form-group">
<input
type="text"
placeholder="Name"
name="name"
value={name}
onChange={event => onChange(event)}
/>
</div>
<div className="form-group">
<input
type="email"
placeholder="Email Address"
name="email"
value={email}
onChange={event => onChange(event)}
/>
<small className="form-text">
This site uses Gravatar so if you want a profile image, use a
Gravatar email
</small>
</div>
<div className="form-group">
<input
type="password"
placeholder="Password"
name="password"
// minLength="6"
value={password}
onChange={event => onChange(event)}
/>
</div>
<div className="form-group">
<input
type="password"
placeholder="Confirm Password"
name="password2"
value={password2}
onChange={event => onChange(event)}
/>
</div>
<input type="submit" className="btn btn-primary" value="Register" />
</form>
<p className="my-1">
Already have an account? <Link to="/login">Sign In</Link>
</p>
</Fragment>
);
};
In React Native, that is with a different professor, I tried to think how to do this. I tried a few days the offered props from the TextInput but non of then, in my opinion, can be used how we can use the "name" attribute in ReactJS.
This is the code for the React Native app:
import React, { Component } from 'react';
import {
StyleSheet,
View,
Button,
TextInput,
} from 'react-native';
class PlaceInput extends Component {
state = {
userName: '',
placeName: ''
}
userNameChangeHandler = (value) => {
this.setState({ userName: value })
}
placeNameChangeHandler = (value) => {
this.setState({ placeName: value })
}
placeSubmitHandler = () => {
if (this.state.placeName.trim() === '') {
return;
}
this.props.onPlaceAdded(this.state.placeName)
}
render() {
return (
<View style={styles.inputContainer}>
<TextInput
style={styles.placeInput}
value={this.state.userName}
onChangeText={this.userNameChangeHandler}
placeholder='User Name' />
<TextInput
style={styles.placeInput}
value={this.state.placeName}
onChangeText={this.placeNameChangeHandler}
placeholder='Beautiful place' />
<Button title='Add' style={styles.placeButton} onPress={this.placeSubmitHandler} />
</View>
);
}
};
Please someone help me to understand: it is possible to have one onChangeText method in React Native, like the professor from ReactJS did with onChange?
Try passing input "name" as a value to the handler function.
Like so:
import React, { Component } from 'react';
import {
StyleSheet, View, TextInput,
} from 'react-native';
class PlaceInput extends Component {
state = {
userName: '',
placeName: ''
}
handleInputChange = (inputName, inputValue) => {
this.setState(state => ({
...state,
[inputName]: inputValue // <-- Put square brackets
}))
}
render () {
return (
<View style={styles.inputContainer}>
<TextInput
style={styles.placeInput}
value={this.state.userName}
onChangeText={value => this.handleInputChange('userName', value)}
placeholder='User Name' />
<TextInput
style={styles.placeInput}
value={this.state.placeName}
onChangeText={value => this.handleInputChange('placeName', value)}
placeholder='Beautiful place' />
</View>
);
}
};
For that type of a function you are using the wrong prop. While onChangeText is all fine, it takes as its parameter a function with a single parameter: the changed text. Because of that you cannot do it with the onChangeText.
However, there exists another prop called onChange. This one supplies the following object as the parameter per the documentation: { nativeEvent: { eventCount, target, text} }. Target here, while could be used, is going to be just a number.
What would I suggest?
Instead of trying to handle it through the event.target.name change your function to take a second argument: name. After that you should call your functions as follows;
onChangeText={text => this.inputChangeHandler(text, 'name')}
This will create a function whose sole purpose is to supply the second parameter, allowing you to use just one function for all your text changes.

React-datepicker with a Formik form

I'm trying to use react-datepicker in a Formik form.
I have:
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
class Fuate extends React.Component {
state = {
dueDate: new Date()
}
<Formik
initialValues={initialValues}
validationSchema={Yup.object().shape({
title: Yup.string().required("A title is required "),
})}
onSubmit={this.handleSubmit}
render={({
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
dirty,
values
}) => {
return (
<div>
...
<DatePicker
name={'dueDate'}
value={values['dueDate']}
onChange={e => setFieldValue('dueDate', e)}
/>
<DatePicker
style={{ width: 180 }}
date={values.dueDate}
mode="date"
format="YYYY-MM-DD"
minDate={Date.now.toString()}
maxDate="2050-06-01"
confirmBtnText="Confirm"
cancelBtnText="Cancel"
showIcon={false}
customStyles={{
dateInput: {
marginLeft: 0,
borderColor: "#fff"
}
}}
onDateChange={date => setFieldValue("dueDate", date)}
onTouch={setFieldTouched}
/>
For both of these options, the form renders, I can select a date on the calendar but it does not appear in the box and the state value is not updated with the selection.
There are no errors in the console, but the warning says:
Starting with v2.0.0-beta.1 date-fns doesn't accept strings as
arguments. Please use parseISO to parse strings. See:
toDate # index.js:45
I tried making the initial state:
dueDate: new Date().toISOString(),
but it makes no difference.
I've seen lots of posts about setting this up with Antd's date picker, but can't find instructions for how to do it with react-datepicker.
Update to Dani Vijay's answer.
This uses useField and useFormikContext from Formik v2, to simplify usage of the component.
DatePicker.jsx:
import React from "react";
import { useField, useFormikContext } from "formik";
import DatePicker from "react-datepicker";
export const DatePickerField = ({ ...props }) => {
const { setFieldValue } = useFormikContext();
const [field] = useField(props);
return (
<DatePicker
{...field}
{...props}
selected={(field.value && new Date(field.value)) || null}
onChange={val => {
setFieldValue(field.name, val);
}}
/>
);
};
Usage (see Dani's answer for the complete form declaration):
...
<DatePickerField name="date" />
...
Code at codesandbox
react-datepicker can used with Formik by utilising setFieldValue,
const DatePickerField = ({ name, value, onChange }) => {
return (
<DatePicker
selected={(value && new Date(value)) || null}
onChange={val => {
onChange(name, val);
}}
/>
);
};
const App = () => (
<Formik
initialValues={{ date: "" }}
...
>
{props => {
const {
values,
handleSubmit,
setFieldValue
...
} = props;
return (
<form onSubmit={handleSubmit}>
<DatePickerField
name="date"
value={values.date}
onChange={setFieldValue}
/>
...
CodeSandbox demo here
Update to Dani Vijay and ToolmakerSteve's answers:
Use setValue directly instead of setFieldValue. Based on #asologor's comment.
import React from "react";
import { useField } from "formik";
import DatePicker from "react-datepicker";
export const DatePickerField = ({ ...props }) => {
const [field, , { setValue }] = useField(props);
return (
<DatePicker
{...field}
{...props}
selected={(field.value && new Date(field.value)) || null}
onChange={(val) => {
setValue(val);
}}
/>
);
};
Note: This method lets you to use Formik with react-datepicker, but it also works for other UI Libraries like Material-UI and Ant Design too.
What I see from your code is DatePicker is not inside of Formik. In React.js we always create reusable components so keep the code clean and easy to test. To be able to use DatePicker in your code, create a separate file, write your code for Datepicker and render it as a component of the Field. here is how you should implement.
//First let's start with structure of Formik.
import PortDate from "./form/PortDate";
//define your components in a different directory. I named it form
const CreateForm = props => (
<div>
<Formik
initialValues={{"your initial values"}}
validate={your validation function here}
onSubmit={values => {
return props.onSubmit(values);
}}
>
{({ isSubmitting }) => (
<Form>
<Field name="startDate" component={DatePicker} label="Start Date" />
<Field
name="endDate"
component={DatePicker}
label="End Date"
canBeDisabled={true}
/>
</Form>
)}
</Formik>
</div>
);
now in a different component let's implement our Datepicker logic.
//Use reactstrap instead of bootstrap when you want to add style
import { FormGroup, Label } from "reactstrap";
class DatePicker extends React.Component {
constructor(props) {
super(props);
this.state = {
dueDate: new Date(),
};
this.handleChange = this.handleChange.bind(this);
}
setFieldValueAndTouched(date, touched) {
const { setFieldValue, setFieldTouched } = this.props.form;
const { name } = this.props.field;
setFieldValue(name, date, true); //field,value,shouldValidate
setFieldTouched(name, touched, true); //field,touched,shouldValidate
}
handleChange(date) {
this.setState(() => ({ dateValue: date }));
this.setFieldValueAndTouched(date, true); }
render() {
const { dueDate } = this.state;
const {
label,
field,
form: { touched, errors },
} = this.props;
// field and form props are passed to this component automatically because we render this inside component of the Field.
return (
<FormGroup>
<Label>{label}</Label>
<div className="input-group">
<DatePicker
selected={dueDate}
onChange={this.handleChange}
peekNextMonth
showMonthDropdown
showYearDropdown
maxDate={new Date()}
dropdownMode="select"
/>
</div>
</FormGroup>
);
}
}
export default DatePicker;
How to make this to work for the date range picker:
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const { setFieldValue } = useFormikContext();
const [field] = useField({ name: name, value: startDate });
const [field2] = useField({ name: name2, value: endDate });
<GroupContainer>
<DatePicker
{...field}
onFocus={() => setFocusStart(true)}
onCalendarClose={() => setFocusStart(false)}
selected={(field.value && new Date(field.value)) || null}
onChange={(val) => {
setStartDate(val);
setFieldValue(field.name, val);
}}
dateFormat="dd.MM.yyyy"
selectsStart
minDate={new Date()}
/>
</GroupContainer>
<GroupContainer>
<DatePicker
{...field2}
onFocus={() => setFocusEnd(true)}
onCalendarClose={() => setFocusEnd(false)}
selected={(field2.value && new Date(field2.value)) || null}
onChange={(val) => {
setEndDate(val);
setFieldValue(field2.name, val);
}}
dateFormat="dd.MM.yyyy"
selectsEnd
minDate={new Date()}
/>
</GroupContainer>
Then:
<ReactDatePicker name="arrivalDate" name2="departureDate" />
const formik = useFormik({
initialValues: {
date:""
},
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
});
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
variant="inline"
TextFieldComponent={(params) => {
return <TextField className={classes.TextField}
{...params} variant="outlined" />;
}}
format="dd MMM yyyy"
margin="normal"
name="installDate"
value={formik.values.date}
onChange={(newDate) => {
//use this here
formik.setFieldValue("date", newDate);
}}
KeyboardButtonProps={{
"aria-label": "change date",
}}
/>
</MuiPickersUtilsProvider>

ReactJS - How can I set a value for textfield from material-ui?

I have a selectField and I want to set a value on it. Let say I type on it and when I click a button, the button will call a function that will reset the value of the textfield?
<TextField hintText="Enter Name" floatingLabelText="Client Name" autoWidth={1} ref='name'/>
You can do it in this way
export default class MyCustomeField extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Enter text',
};
}
handleChange = (event) => {
this.setState({
value: event.target.value,
});
};
handleClick = () => {
this.setState({
value:'',
});
};
render() {
return (
<div>
<TextField
value={this.state.value}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>Reset Text</button>
</div>
);
}
}
It's maintained that the right way is to have the component be controlled in a scenario like the accepted answer there, but you can also control the value in this gross and culturally unacceptable way.
<TextField ref='name'/>
this.refs.name.getInputNode().value = 'some value, hooray'
and you can of course retrieve the value like so
this.refs.name.getValue()
Instead of using ref you should use inputRef
const MyComponent = () => {
let input;
return (
<form
onSubmit={e => {
e.preventDefault();
console.log(input.value);
}}>
<TextField
hintText="Enter Name"
floatingLabelText="Client Name"
autoWidth={1}
inputRef={node => {
input = node;
}}/>
</form>
)
};
Here is a short simple React Hook version of the answer
export default function MyCustomeField({
initialValue= '',
placeholder= 'Enter your text...'
}) {
const [value, setValue] = React.useState(initialValue)
return (
<div>
<TextField
placeholder={placeholder}
value={value}
onChange={e => setValue(e.target.value)}
/>
<button onClick={() => setValue(initialValue)}>Reset Text</button>
</div>
);
}
I would suggest to do a little wrap on the original MUI TextField.
export default function ValueTextField(props) {
const [value, setValue] = useState(props.value);
return (
<TextField {...props} onChange={(event) => setValue(event.target.value)} value={value}/>
);
}
Now you can use your own ValueTextField component now.
<ValueTextField value={"hello world"}></ValueTextField>
I prefer this way to assign the state variables:
<TextField value={mobileNumber}
onChange={e => { this.setState({ mobileNumber: e.target.value }) }}
className={classes.root}
fullWidth={true}
label={t('mobile number')} />

Resources