Open benneq opened 5 years ago
I think you can use state. To pass with onValueChange values.value to some variable in your state. And then use is with your onChange function.
You mean that I should listen to both onValueChange
and onChange
? Is is guaranteed, that onValueChange
is getting called first?
And then, you mean something like this?
onValueChange={values =>
this.setState({value: values.floatValue})
}
onChange={e => {
e.persist();
e.target = {
...e.target,
value: this.state.value
}
})
Yes onValueChange will always be called before onChange. But value can change on blur event as well which will trigger onValueChange but not onChange. Even onValueChange will be called when there is a prop change, in that case there is no event involved. That's why onValueChange does not receive any event object.
Formik
has setFieldValue method which you should use for your case.
See example of using Formik with 3rd Party Input Component
https://codesandbox.io/s/73jj9zom96
Here example is with the react-select. But same thing can be done for react-number-format.
I have a similar problem: I dynamically generate the input fields with React.CreateElement. The idea is that the generated input fields should have a common onValueChange eventHandler. Since onValueChange does not return the event, it is not possible to identify the component that is the source of the onValueChange. This was possible earlier when the event was returned as part of onValueChange. One possibility is to use onChange, but then one needs an "unformat" utility function. Another option is to reintroduce the event as part of onValueChange.
That's a general "problem" of the React ecosystem. For my apps I generally build wrapper components for nearly everything. This means: I have an own component library, which internally uses 3rd party components like material, number-format, formik, etc... And in the app's code I use my own component library.
For my purpose with Formik
I created a pair of components, which handle this for me. It doesn't use onChange
of NumberFormat
.
NumberField.tsx (which is a simple wrapper for React Material):
type NumberFieldProps = Omit<TextFieldProps, 'type' | 'value' | 'InputProps'> & {
value: number
onValueChange?: (values: NumberFormatValues) => void
allowNegative?: boolean
decimalScale?: number
InputProps?: Omit<TextFieldProps["InputProps"], 'inputComponent'>
};
const NumberFormatCustom = (props: any) => {
const { inputRef, onValueChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={onValueChange}
thousandSeparator={'.'}
decimalSeparator={','}
/>
);
}
const NumberField: React.FunctionComponent<NumberFieldProps> = (props) => {
const { inputProps, InputProps, onValueChange, allowNegative, decimalScale, ...rest } = props;
return (
<TextField
inputProps={{
...inputProps,
onValueChange: onValueChange,
allowNegative: allowNegative,
decimalScale: decimalScale
}}
InputProps={{
...InputProps,
inputComponent: NumberFormatCustom
}}
{...rest}
/>
);
}
And then I have my FormNumberField.tsx, which is a wrapper using Formik + my NumberField component:
type Props = Omit<NumberFieldProps, 'error' | 'onChange' | 'onBlur' | 'value' | 'component'> & {
component?: React.ComponentType<NumberFieldProps>
}
const FormNumberField: React.FunctionComponent<Props> = (props) => {
const { name, component: Component = NumberField, helperText, ...rest } = props;
return (
<Field
name={name}
render={({field, form: { touched, errors, setFieldValue }}: FieldProps) => {
const fieldError = getIn(errors, name);
const showError = getIn(touched, name) && !!fieldError;
return (
<Component
error={showError}
helperText={showError ? fieldError : helperText}
onValueChange={values => setFieldValue(name, values.floatValue)}
{...field}
onChange={undefined}
{...rest}
/>
)
}
}/>
);
}
I ended up with a wrapper as well:
import React from 'react';
import PropTypes from 'prop-types';
import NumberFormat from 'react-number-format';
import {TextField} from './text-field';
const noop = () => {};
export const NumberField = ({
id,
name,
onValueChange,
...otherProps
}) => {
const handleValueChange = (valueObject) => onValueChange(id, name, valueObject);
return (
<NumberFormat id={id} name={name} {...otherProps} customInput={TextField} onValueChange={handleValueChange} />
);
};
NumberField.propTypes = {
/**
* Specify an `id` for the <input>
*/
id: PropTypes.string.isRequired,
/**
* Provide a name for the underlying <input> node
*/
name: PropTypes.string,
/**
* Provide an optional `onValueChange` hook that is called each time the
* the underlying <input> value changes. Accepts `id`, `name` and `valueObject`
*/
onValueChange: PropTypes.func,
};
NumberField.defaultProps = {
onValueChange: noop,
};
I have one concern, I need to work on onchange. While typing some value i need to generate some other value. How can i achieve this.
on basis of min/max the range will be calculated, so it was working fine if i dont change raidio button, but when am changing radion button again onvaluechange was getting called.
how can i work using onChange function
onValueChange gets called when the prop changes and cause format value to change. Most of the time that is the usecase, I am not sure whats the expected result you are looking for here, If you can tell whats the behavior and whats the expected behaviour, I can help you better.
Is there a way to get the whole
ChangeEvent
with the unformatted value (or float value)?I'm using
formik
and therefore need to useonChange
, because it needs theChangeEvent
data to determine which field has changed.The problem is, that
onChange
'sevent.target.value
contains the formatted value. But I need the unformatted value (or float value).<NumberFormat onChange={(event) => { console.log(event); // I need this event console.log(event.target.value); // This is the formatted value, I don't want }} onValueChange={(values) => { console.log(values.value); // Here's the unformatted value, I want console.log(values.floatValue); // Here's the float value, which is okay, too }} prefix="$" />
In other words: I need
onChange
with the value provided byonValueChange
.Is there any way to get this working?
I don't know if this a pretty solution, but it work well for me...
const PrettyInput = ({ onChange, ...rest }) => {
let floatValue = 0;
return (
<NumberFormat
onValueChange={values => (floatValue = values.floatValue)}
onChange={() => onChange(floatValue || '')}
{...rest}
/>
);
};
I think that majority of us have problems when upgrading to v4 when this was changed:
Trigger onValueChange if the value is formatted due to prop change.
While onChange
is triggered only at typing, onValueChange
is triggered at typing and at prop change event. This is crucial! What most of us need is pure value at onChange
listener and this is not possible anymore out of the box. I've tested it @rsilvamanayalle solution and it does work, but we are relying on that onChangeValue
is called before onChange
which is bad.
Is this fixed? Because I can not get floatValue in onChange at the moment in version: 4.3.1. And I can not find change log anywhere.
I'm using formik and I need it too
I see that onValueChanged is called on blur with an object having the keys, but undefined in all of them ({formattedValue: "", value: "", floatValue: undefined}), when number has more than 3 digits and thousandSeparator="," is used. I saved the value from onValueChanged for using later in onChange, it seems onChange is no longer called right after this for blur. I checked this in conjunction with material-ui text field as below.
Any way of by-passing this blur function? It is making my text field unusable with redux-form, as the field is cleared after blur.
import TextField from '@material-ui/core/TextField';
const hasValue = (value) =>
value !== undefined && value !== null;
function MUIReduxFormTextField(props) {
const {
InputProps,
onChange,
...other
} = props;
const fieldInputProps = other.type === 'number'
? { ...InputProps, inputComponent: NumericFormat }
: InputProps;
return (
<TextField
InputProps={fieldInputProps}
onChange={onChange}
{...other}
type={other.type === 'number' ? 'text' : other.type}
/>
);
}
class NumericFormat extends Component {
render() {
const { inputRef, onChange, min, max, ...other } = this.props;
const numericValue = other.value ? Number(other.value) : 0;
return (
<NumberFormat
{...other}
value={numericValue}
getInputRef={inputRef}
onChange={(event) => {
if (this.changed) {
if (onChange) {
onChange(this.changedValue);
}
this.changed = false;
this.changedValue = undefined;
}
}}
onValueChange={(values) => {
this.changed = true;
this.changedValue = values.value;
}}
thousandSeparator=","
decimalSeparator="."
isAllowed={(values) => {
const { floatValue } = values;
return (!hasValue(min) || floatValue >= min) && (!hasValue(max) || floatValue <= max);
}}
/>
);
}
}
@R4DIC4L Can you create a sandbox with the example above. It will be easier to debug that way.
@s-yadav Here is a code sandbox: https://codesandbox.io/s/material-ui-number-format-e1tph?file=/demo.js.
I found the problem, the value returned in the redux-form submit function is sending the formatted text instead of number. I cannot explain this as I am sending the floatNumber in the onChange handler (as can be seen in NumericFormat component from numeric-format.js file). Any ideas why this is happening and if there is any way of making it send the number instead? If I kept the type="number" on NumberFormat, the HTML validation would kick in and not allow comma separator for the thousands.
Edit: I logged the value in the NumericFormat component and it is a number, not a string. It seems that it is changed to a string on blur event.
@s-yadav Please let me know if you want me to add a separate GitHub issue for this, I am not sure it is related to the unformatted value item. Although, it might be related to the onValueChange being triggered on blur without an onChange event with the undefined values.
@R4DIC4L Thanks for the CodeSandbox link. I am investigating this one but in the meantime, as you suggested.. can you please create another issue with the steps to reproduce it..
Based on some preliminary investigation.. I am unable to see onValueChange
being triggered on blur although I am still trying to debug this situation. Let's pick this up in a new issue in order to keep concerns separated. Thanks for your understanding!
@nikhil-varma @s-yadav I have added item https://github.com/s-yadav/react-number-format/issues/554 for the above scenario.
@R4DIC4L Thanks!
I think that majority of us have problems when upgrading to v4 when this was changed:
Trigger onValueChange if the value is formatted due to prop change.
While
onChange
is triggered only at typing,onValueChange
is triggered at typing and at prop change event. This is crucial! What most of us need is pure value atonChange
listener and this is not possible anymore out of the box. I've tested it @rsilvamanayalle solution and it does work, but we are relying on thatonChangeValue
is called beforeonChange
which is bad.
I agree on this, I would suggest having the same props that we have available in the onValueChanges
handle in the onChange
event. This becomes troublesome with controlled inputs because the value of the input in the state might not always change due to a user interaction (useEffect
for example).
@nikhil-varma
Yes onValueChange will always be called before onChange. But value can change on blur event as well which will trigger onValueChange but not onChange. Even onValueChange will be called when there is a prop change, in that case there is no event involved. That's why onValueChange does not receive any event object.
Formik
has setFieldValue method which you should use for your case. See example of using Formik with 3rd Party Input Component https://codesandbox.io/s/73jj9zom96Here example is with the react-select. But same thing can be done for react-number-format.
But onChange
has formatted value not the original value. I don't think your example addresses the actual issue.
onChange={(event: any) => {console.log("version", event?.target?.value)}}
I had quite the same problem with onChangeValue
vs onChange
and had to use onChange wherein the value is being stripped so the value won't return as the formatted one, others might take it as a weird solution but my scenario is when input1
is being changed or typed into, the input2
will also change but without typing and just setting a value depending on the formula and that's vice versa, the problem I had is that onChangeValue
triggers everytime even If I didn't type into it
onChange={(e: any) => {
const stripValue = e.target.value.replace(/[^\d.-]/g, '');
console.log(stripValue)
}}
onValueChange gets called when the prop changes and cause format value to change. Most of the time that is the usecase, I am not sure whats the expected result you are looking for here, If you can tell whats the behavior and whats the expected behaviour, I can help you better.
There is a slight difference between: a) User is making a change to a state property via the UI b) Application is intializing values from persisted data
In case of a) I expect the app to invoke the "change event" but in case of b) I do not expect the system to invoke the "change event". For a) the corresponding data has been truly changed - for b) the data has not changed, the application is rendering the data as it is existing within the persisted data.
In our case the function invoked by onValueChange is automatically updating the data in the redux store and is also setting some flag indicating that the data was modified which leads to a button "Save Modifcations" & "Cancel Modifications" to appear on top of some list view. Obviously we do not want to flag that data has been changed when setting the intial values of the component.
onChange works as expected but only delivers the formatted value.. now we need to implement some hack to differenciate the user case a) & b) and make sure that the redux store is not updated in case of b).
The workaround to use the combination of onValueChange & onChange works - but this is not straight forward to me.
Same issue, if using a format library and then cannot get original value, it will be useless. Have anyone suggest another solution or library?
Is there a way to get the whole
ChangeEvent
with the unformatted value (or float value)?I'm using
formik
and therefore need to useonChange
, because it needs theChangeEvent
data to determine which field has changed.The problem is, that
onChange
'sevent.target.value
contains the formatted value. But I need the unformatted value (or float value).In other words: I need
onChange
with the value provided byonValueChange
.Is there any way to get this working?