I have a Material-Ui TextField handled with Formik.
Input value (string) is converted to number on Input Change.
My problem is that when the value number zero passes, it's considered as a false value and renders an empty string.
I want it to get 'number zero' showing in TextField.
If I removes TextField value condition ( value || ' ' ), It will give me a warning message below.
Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.
How can I work around with it ?
input.js
const Input = ({
classes,
icon,
styleName,
field: { name, value, onBlur },
form: { errors, touched, setFieldValue },
...props
}) => {
const errorMessage = getIn(errors, name);
const isTouched = getIn(touched, name);
const change = (e, name, shouldValidate) => {
e.persist();
const inputValue = e.target.value;
let value;
if (inputValue !== '') {
value = isNaN(inputValue) ? inputValue : parseInt(inputValue, 10);
} else {
value = null;
}
return setFieldValue(name, value, shouldValidate);
};
return (
<TextField
name={name}
value={value || ''}
onChange={e => change(e, name, true)}
onBlur={onBlur}
{...props}
className={classes[styleName]}
helperText={isTouched && errorMessage}
error={isTouched && Boolean(errorMessage)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Icon
name={icon}
width="30"
height="30"
viewBox="0 0 30 30"
fill="none"
/>
</InputAdornment>
),
}}
/>
);
};
I experienced a situation like this in some of our projects.
This isn't specific to Material-UI but to react.
To work around this, just set the initial value to an empty string ''.
So far we're fine with setting the value to an empty string since it's the default event.target.value of input type number when it's empty.
See: https://codesandbox.io/s/affectionate-stonebraker-cgct3
The suggested solution did't work for me.
Number 0 is falsy. so it renders an empty string.
I resolved it with this approach.
const input = value === 0 || value ? value : '';
return (
<TextField
name={name}
value={input}
...
/>
);
Related
check this code please https://stackblitz.com/edit/react-koqfzp?file=src/Section.js
Everytime i add an item, i'm also adding an random number that i want to edit. The number is rendered at a MUI Text field component.
<TextField
type="number"
variant="standard"
aria-readonly={edit === true ? false : true}
value={edit === true ? value : numbers[i]}
onChange={(e) => setValue(e.target.value)}
/>
And the buttons are rendered based on edit's state, like this:
{edit === true ? (
<button onClick={() => saveEdit(i)}>Save</button>
) : (
<button onClick={() => setEdit(true)}>Edit</button>
)}
And it's working, i can edit it and rerender with the new value, the problem is that when i click the edit button, every field receives the new input value and the save button shows up in every field, though when i save it, it returns to it's original value. How i can do what i'm doing but to just one specific field?
The problem is that you are using setEdit as a boolean
You can define it as the index of the array to edit with -1 as starting value
const [edit, setEdit] = useState(-1)
...
{edit === i ? (
<button onClick={() => saveEdit(i)}>Save</button>
) : (
<button onClick={() => setEdit(i)}>Edit</button>
)}
I recommend creating another component Exemple:Item with it has it's own states edit,value states
And pass the needed props to it which are the value,numbers,saveEdit(index,newValue),removeItem(index) and also the index
saveEdit has to be changed in section by passing the index and the newValue
I hope this clear for you
add the map values in a component and add the states inside the component so you will have multiple states for each component not just 1
parent component
{section.items.map((item, i) => (
<Component key={i} item={item}/>
)}
child component
const Component = ({ section, addItem, removeItem}) => {
const [newItem, setNewItem] = useState('');
const [edit, setEdit] = useState(false);
const [value, setValue] = useState(0);
const [numbers, setNumbers] = useState(section.number);
const handleChange = (e) => {
setNewItem(e.target.value);
};
return (
<TextField
type="number"
variant="standard"
aria-readonly={edit === true ? false : true}
value={edit === true ? value : numbers[i]}
onChange={(e) => setValue(e.target.value)}
/>
)
I have below code where I am checking or unchecking the checkbox based on some conditions, and I came across an issue where I am trying to check the checkbox, and it is failing the first time and from the second time onwards works perfectly.
export const AntdDefaultOverrideInputNumberAdapter = ({
input: { onChange, value },
disabled,
defaultValue,
formatter,
...rest
}) => {
const [isDefaultValueOverriden, setIsDefaultValueOverriden] = useState(
!!value && value !== defaultValue
);
const handleCheckboxChange = () => {
const hasOverride = !isDefaultValueOverriden;
setIsDefaultValueOverriden(hasOverride);
onChange(hasOverride && !!value ? value : defaultValue);
};
const handleOverrideChange = (v) => {
onChange(v);
};
return (
<Badge
offset={[0, -6]}
count={
<div>
<Checkbox
disabled={disabled}
checked={isDefaultValueOverriden}
onChange={handleCheckboxChange}
style={{ zIndex: 10 }}
/>
</div>
}
>
<InputNumber
size="small"
onChange={handleOverrideChange}
disabled={disabled || !isDefaultValueOverriden}
style={{ width: 65 }}
value={isDefaultValueOverriden ? value : defaultValue}
formatter={formatter}
{...rest}
/>
</Badge>
);
};
I am not sure where I am wrong with the above code, The problem only appears on trying to check the checkbox the first time, and it disappears from the second time onwards..
Could anyone suggest any idea on this? Many thanks in advance!!
I am using the "ANTD" library for the checkbox, and the "value" is an empty string, and the defaultValue is "5"
Issue summary
I get different errors at different times. When I select a suggested option, I get the following error and warning:
Material-UI: The getOptionLabel method of Autocomplete returned undefined instead of a string for 0
Material-UI: The value provided to Autocomplete is invalid. None of the options match with 0
Plus, the option doesn't get selected and the input becomes undefined. However, when trying to choose a value a second time, it gets selected (but still shows the errors).
When I clear the input I get this error:
A component is changing the controlled value state of Autocomplete to be uncontrolled.
Elements should not switch from uncontrolled to controlled (or vice versa).
Decide between using a controlled or uncontrolled Autocomplete element for the lifetime of the component.
The nature of the state is determined during the first render, it's considered controlled if the value is not `undefined`.
Code for the autocomplete component
const AutocompleteUnit = ({control, label, name, ...rest}) => {
return (
<>
<Controller
onChange={([,data]) => data}
name={name}
as={
<Autocomplete
{...rest}
autoHighlight
style={{marginTop: "25px"}}
getOptionLabel={option => option.label}
renderInput={params => (
<TextField
{...params}
label={label}
variant="outlined"
/>
)}
/>
}
control={control}
defaultValue={rest.options[0]}
/>
</>
}
Options
const districtOptions = [
{ value: "ciutatvella", label: "Ciutat Vella" },
{ value: "gracia", label: "Grà cia" },
{ value: "eixample", label: "L'Eixample" },
{ value: "sarria", label: "Sarrià " }
];
Any idea on what's wrong?
just in case some stumbles upon this: you have to use defaultValue of Autocomplete instead of the Controller
Hey just use this package below:
https://www.npmjs.com/package/mui-react-hook-form-plus
import { HookAutoComplete, useHookForm } from 'mui-react-hook-form-plus ';
const Component = () => {
const {registerState, handleSubmit} = useHookForm({
defaultValues: { movie: '' },
});
const onSubmit = (data: typeof defaultValues) => {
// will run if it is valid
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<HookAutoComplete
{...registerState('movie')}
autocompleteProps={{
options: ['Fight Club', 'Top Gun', 'Titanic']
}}
textFieldProps={{
label: 'Movie',
placeholder: 'search...',
}}
/>
<button type='submit'>Submit</button>
</form>
)
}
See Demo
I have a react component as :
const CustomDatePicker = ({ errors, id, name, control, rules, getValues, minDate, maxDate, placeholder, required, defaultValue, ...rest }) => {
console.log("required 1", name, required);
const inputRef = React.useRef();
const validateField = () => {
console.log("required 2", required, name)
if (required && !getValues(name)) {
return false
}
else if (getValues(name)) {
let dateObj = typeof (getValues(name)) == 'string' ? new Date(getValues(name)) : getValues(name)
return !isNaN(dateObj);
}
else return true;
}
return (
<div className="form-group custom-input-container">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<Controller
name={name}
margin="normal"
fullWidth
variant="outlined"
onFocus={() => {
if (inputRef.current) {
inputRef.current.focus()
}
}}
defaultValue={defaultValue}
as={<KeyboardDatePicker
inputRef={inputRef}
className="custom-date-col"
fullWidth
autoOk
clearable
variant="inline"
inputVariant="outlined"
placeholder={placeholder}
minDate={minDate}
format="dd-MM-yyyy"
maxDate={maxDate}
/>}
rules={{
validate: validateField
}}
control={control}
errors={errors}
{...rest}
/>
</MuiPickersUtilsProvider>
</div>
);
};
export default CustomDatePicker;
and a parent component uses the above as follows :
export default function Dummy(props) {
const [req,setReq]=useState(false);
return (
<div className="FamilyDetails__emergency-form-row FamilyDetails__three-col-grid">
<CustomDatePicker
name={`marriageDate`}
errors={props.errors}
control={props.control}
maxDate={new Date()}
minDate={getMinDate()}
placeholder={"dateOfMarriageLbl"}
required={req}
defaultValue={new Date()}
/>
</div>
);
}
I use the above components in the form. Initially "required" prop is going to be false then later it will be changed to true. When I submit the form validateField method gets called and the required prop value in console is printed as false whereas the original value is true. Then console printed outside the function prints "require" prop value as true. The value of "required" prop in validateField function is taking the initial value with which component is initially rendered. Please help me through this.
It looks like react-hook-forms caches validation and the issue has been resolved in latest versions. find the discussion here. This has solved my issue.
I have the following: ( http://catamphetamine.github.io/react-phone-number-input/ )
type PhoneNumberInputProps = {
dataBdd: string
disabled: boolean | undefined,
label?: string,
id: string
onChange: any
value: string
error?: any
maxLength?: number
}
const PhoneNumberInput: React.FC<PhoneNumberInputProps> = ({
dataBdd,
disabled,
id,
onChange,
label,
value,
error
}) => {
const phoneInputStyle = classNames({
'error': error,
'hide': disabled
});
const [updatedValue, setUpdatedValue] = useState(value)
useEffect(() => {
setUpdatedValue(value)
}, [value])
const DEFAULT_COUNTRY_VALUE = 'GB'
const handleOnChange = (val: string) => {
setUpdatedValue(val)
onChange(val)
}
return (
<>
<Label htmlFor={id}>{label}</Label>
<PhoneInput
data-bdd={dataBdd}
disabled={disabled}
id={id}
defaultCountry={DEFAULT_COUNTRY_VALUE}
value={updatedValue}
onChange={handleOnChange}
className={phoneInputStyle}
maxLength={15}
/>
</>
)
}
export default PhoneNumberInput;
if I change the value of a phone number the ui gets updated correctly and the value property gets the + in front of the number which is what I send to the BE.
But, if there is a default value coming from the BE and I don't change that number so it doesn't get formatted the value gets sent without the +.
Is it possible to format the value regardless of the onChange being triggered?
Format the Value before setting the state in useEffect.
import PhoneInput, { formatPhoneNumber, formatPhoneNumberIntl } from 'react-phone-number-input'
<PhoneInput
placeholder="Enter phone number"
value={value}
onChange={setValue}/>
National: {value && formatPhoneNumber(value)}
International: {value && formatPhoneNumberIntl(value)}