React-number-format: Unformatted value (or floatValue) within onChange

Created on 31 Dec 2018  路  12Comments  路  Source: s-yadav/react-number-format

Is there a way to get the whole ChangeEvent with the unformatted value (or float value)?

I'm using formik and therefore need to use onChange, because it needs the ChangeEvent data to determine which field has changed.

The problem is, that onChange's event.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 by onValueChange.

Is there any way to get this working?

Most helpful comment

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.

All 12 comments

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.

image

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 use onChange, because it needs the ChangeEvent data to determine which field has changed.

The problem is, that onChange's event.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 by onValueChange.

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

Was this page helpful?
0 / 5 - 0 ratings