input-number icon indicating copy to clipboard operation
input-number copied to clipboard

最新版本的react-hook-form和rc-input-number是9.3.0版本时出现Illegal invocation错误

Open gavin99587 opened this issue 1 year ago • 3 comments

"react": "18.3.1", "react-dom": "18.3.1", "rc-input-number": "^9.3.0", "@hookform/resolvers": "^3.9.1", "react-hook-form": "^7.54.1",

用以上版本时, 如下代码 import { Box, Form } from 'grommet'; import { z } from 'zod'; import { SubmitHandler, useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import InputNumber from 'rc-input-number';

const zodFormSchema = z.object({ orderQty: z.coerce.number().positive('必须正数'), });

type ZodFormFieldsProps = z.infer<typeof zodFormSchema>; /**

  • 测试inputNumber组件
  • */ export default function TestInputNumber() { const { register, handleSubmit, setValue } = useForm<ZodFormFieldsProps>({ resolver: zodResolver(zodFormSchema), });

const onOrderQtyChange = (val: string | number | null) => { setValue('orderQty', val ? parseFloat(val + '') : 0, { shouldValidate: true, shouldTouch: true, shouldDirty: true, }); }; const onFormSubmit: SubmitHandler<ZodFormFieldsProps> = (formData) => { console.log('formData', formData); };

return ( <Box> <Form onSubmit={handleSubmit(onFormSubmit)}> <InputNumber {...register('orderQty')} min={0} step={1} onChange={onOrderQtyChange} /> </Form> </Box> ); }

会报 hook.js:608 React Router caught the following error during render TypeError: Illegal invocation at Proxy.set (react-dom.development.js:1661:1)错误 但是如果将"rc-input-number": "^9.0.0",改为此版本,上面其他内容都不变,则不会报错,使用正常

gavin99587 avatar Dec 15 '24 15:12 gavin99587

My workaround is to use Controller instead of register

❌
<NumberInput {...register('price')} /> 

✅
<Controller render={({field: {value, onChange}}) => (
  <NumberInput value={value} onChange={onChange}/>
)}/>  

layerok avatar Sep 10 '25 09:09 layerok

I recreated the issue. You can paste below code into the browser console and see the same error. I slightly modified trackValueOnNode function but it is essentially what is happening under the hood.

const trackValueOnNode = (node) => {
  const valueField = 'value'
  const descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField);

  let currentValue = '' + node[valueField]; 

  if (node.hasOwnProperty(valueField) || typeof descriptor === 'undefined' || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') {
    return;
  }

  const get = descriptor.get,
      set = descriptor.set;
  Object.defineProperty(node, valueField, {
    configurable: true,
    get: function () {
      return get.call(this);
    },
    set: function (value) {
      currentValue = '' + value;
      set.call(this, value);
    }
  });
}

const node = document.createElement('input');
trackValueOnNode(node);

const proxy = new Proxy(node, {});
proxy.value = 'f'

The issue could have been prevented if NumberInput didn't wrap ref in a proxy. Or if react didn't track value on node.

layerok avatar Sep 10 '25 09:09 layerok

React just doesn't expect that ref is overwritten with a ref that is wrapped in a proxy.

layerok avatar Sep 10 '25 09:09 layerok