-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(useCustomEnsuredControl/useFloatingWithInteractions): Initialize preservedControlledValueRef with value #7099
Conversation
We got an error from users about'shown' in useFloatingWithInteractions hook TypeError: Uncaught TypeError: Cannot read properties of undefined (reading 'shown') I wasn't able to catch this locally, or in test env, but to make sure preservedControlledValueRef can't be undefined if value is defind we give it a value.
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
size-limit report 📦
|
e2e tests |
👀 Docs deployed
Commit 406784a |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #7099 +/- ##
=======================================
Coverage 83.78% 83.78%
=======================================
Files 357 357
Lines 10676 10676
Branches 3551 3551
=======================================
Hits 8945 8945
Misses 1731 1731
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💅
We got an error from users about'shown' in useFloatingWithInteractions hook TypeError: Uncaught TypeError: Cannot read properties of undefined (reading 'shown') I wasn't able to catch this locally, or in test env, but to make sure preservedControlledValueRef can't be undefined if value is defind we give it a value.
We got an error from users about'shown' in useFloatingWithInteractions hook TypeError: Uncaught TypeError: Cannot read properties of undefined (reading 'shown') I wasn't able to catch this locally, or in test env, but to make sure preservedControlledValueRef can't be undefined if value is defind we give it a value. Co-authored-by: Andrey Medvedev <andr.medv.spb@gmail.com>
✅ v6.2.1 🎉 |
… reveal prevValue undefined (#7285) Выяснилось, что всё-таки, если передавать в свойство `onChange` `useCustomEnsuredControl` коллбэк `nextValue` c аргументом `prevValue`, то есть вероятность, что `prevValue` будет `undefined`. Это видно, если исправить тип аргумента prevValue с `any` на `V`. https://github.com/VKCOM/VKUI/blob/a7b79b3b3a69bdf68163f62521f68697aceb52c2/packages/vkui/src/hooks/useEnsuredControl.ts#L56 Дело в том, что `preservedControlledValueRef`, которое мы передаём в `nextValue` коллбэк как значение `prevValue`, может быть `undefined`, так как хранит предыдущее значение `value`, которое тоже может быть `undefined`. https://github.com/VKCOM/VKUI/blob/a7b79b3b3a69bdf68163f62521f68697aceb52c2/packages/vkui/src/hooks/useEnsuredControl.ts#L70-L73 В теории (потому что в тестах не удалось повторить), если пользователь в `useCustomEnsuredControl` не которолирует значение `value` (то есть проп `value === undefined`, а мы храним значение `value` локально в `useCustomEnsuredControl`), а затем делает `value` котролируемым (то есть начинает передавать в проп `value` какое-то значение), то есть вероятность, что при вызове `onChanage` `preservedControlledValueRef` ещё не обновился и равен предыдущему значение `value`, то есть `undefined`, тогда пользователь получит `prevValue` со значением `undefined`, что может привести к ошибке, как описано в #7099 > TypeError: Uncaught TypeError: Cannot read properties of undefined (reading 'shown') ## Изменения - Исправили тип `prevValue` в колбэке `nextValue`, заменив `any`, чтобы была видна проблема c undefined. Но теперь ясно, что мы не можем просто так передать `preservedControlledValueRef` в `nextValue` коллбэк в качестве `prevValue`, потому что типы не сходятся. Что можно сделать. 1. Просто не вызывать `nextValue`, если `preservedControlledValueRef` `undefined`, но тогда мы получаем риск не отреагировать на действие пользователя, так как `onChange` не будет вызван и состояние не поменяется. 2. Разрешить `prevValue` по типам быть `undefined`. Но тогда пользователи, использующие в `onChange` коллбэк `nextValue` получат по типам вариант, что `prevValue` может быть `undefined`. Это очень странное состояние и тяжело объяснить его причины. А также тяжело принять решение при обработке onChange. Например, в `useChipsInput` не понятно откуда тогда взять актуальный массив `chips`, чтобы добавить в него новое значение. https://github.com/VKCOM/VKUI/blob/7755cabb003c2dac894703ac30335c0b874d8d52/packages/vkui/src/components/ChipsInput/useChipsInput.ts#L107-L126 3. (Текущее решение) Если `preservedControlledValueRef` `undefined`, то использовать значение `value`. Так как мы знаем, что компонент котролируемый, значит `value` имеет какое-то значение. При переходе из некоторолируемого значения в контролируемое у `value` нету предыдущего состояния, поэтому самым адекватным решением будет предать текущее значение `value` как `prevValue`. Чтобы `value` не находилось в массиве зависимостей `onChange` и не тригерило новое значение `onChange` при изменении, мы `value` прячем в `ref`, и используем лишь тогда, когда `preservedControlledValueRef` === `undefined`. - Добавили warning. Хотелось ещё дать ссылку на [React warning](https://react.dev/reference/react-dom/components/input#im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled), тогда надо пояснять, что в нашем случае речь не только про input, а как это сделать лаконично я не придумал.
… reveal prevValue undefined (#7285) Выяснилось, что всё-таки, если передавать в свойство `onChange` `useCustomEnsuredControl` коллбэк `nextValue` c аргументом `prevValue`, то есть вероятность, что `prevValue` будет `undefined`. Это видно, если исправить тип аргумента prevValue с `any` на `V`. https://github.com/VKCOM/VKUI/blob/a7b79b3b3a69bdf68163f62521f68697aceb52c2/packages/vkui/src/hooks/useEnsuredControl.ts#L56 Дело в том, что `preservedControlledValueRef`, которое мы передаём в `nextValue` коллбэк как значение `prevValue`, может быть `undefined`, так как хранит предыдущее значение `value`, которое тоже может быть `undefined`. https://github.com/VKCOM/VKUI/blob/a7b79b3b3a69bdf68163f62521f68697aceb52c2/packages/vkui/src/hooks/useEnsuredControl.ts#L70-L73 В теории (потому что в тестах не удалось повторить), если пользователь в `useCustomEnsuredControl` не которолирует значение `value` (то есть проп `value === undefined`, а мы храним значение `value` локально в `useCustomEnsuredControl`), а затем делает `value` котролируемым (то есть начинает передавать в проп `value` какое-то значение), то есть вероятность, что при вызове `onChanage` `preservedControlledValueRef` ещё не обновился и равен предыдущему значение `value`, то есть `undefined`, тогда пользователь получит `prevValue` со значением `undefined`, что может привести к ошибке, как описано в #7099 > TypeError: Uncaught TypeError: Cannot read properties of undefined (reading 'shown') ## Изменения - Исправили тип `prevValue` в колбэке `nextValue`, заменив `any`, чтобы была видна проблема c undefined. Но теперь ясно, что мы не можем просто так передать `preservedControlledValueRef` в `nextValue` коллбэк в качестве `prevValue`, потому что типы не сходятся. Что можно сделать. 1. Просто не вызывать `nextValue`, если `preservedControlledValueRef` `undefined`, но тогда мы получаем риск не отреагировать на действие пользователя, так как `onChange` не будет вызван и состояние не поменяется. 2. Разрешить `prevValue` по типам быть `undefined`. Но тогда пользователи, использующие в `onChange` коллбэк `nextValue` получат по типам вариант, что `prevValue` может быть `undefined`. Это очень странное состояние и тяжело объяснить его причины. А также тяжело принять решение при обработке onChange. Например, в `useChipsInput` не понятно откуда тогда взять актуальный массив `chips`, чтобы добавить в него новое значение. https://github.com/VKCOM/VKUI/blob/7755cabb003c2dac894703ac30335c0b874d8d52/packages/vkui/src/components/ChipsInput/useChipsInput.ts#L107-L126 3. (Текущее решение) Если `preservedControlledValueRef` `undefined`, то использовать значение `value`. Так как мы знаем, что компонент котролируемый, значит `value` имеет какое-то значение. При переходе из некоторолируемого значения в контролируемое у `value` нету предыдущего состояния, поэтому самым адекватным решением будет предать текущее значение `value` как `prevValue`. Чтобы `value` не находилось в массиве зависимостей `onChange` и не тригерило новое значение `onChange` при изменении, мы `value` прячем в `ref`, и используем лишь тогда, когда `preservedControlledValueRef` === `undefined`. - Добавили warning. Хотелось ещё дать ссылку на [React warning](https://react.dev/reference/react-dom/components/input#im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled), тогда надо пояснять, что в нашем случае речь не только про input, а как это сделать лаконично я не придумал.
… reveal prevValue undefined (#7285) Выяснилось, что всё-таки, если передавать в свойство `onChange` `useCustomEnsuredControl` коллбэк `nextValue` c аргументом `prevValue`, то есть вероятность, что `prevValue` будет `undefined`. Это видно, если исправить тип аргумента prevValue с `any` на `V`. https://github.com/VKCOM/VKUI/blob/a7b79b3b3a69bdf68163f62521f68697aceb52c2/packages/vkui/src/hooks/useEnsuredControl.ts#L56 Дело в том, что `preservedControlledValueRef`, которое мы передаём в `nextValue` коллбэк как значение `prevValue`, может быть `undefined`, так как хранит предыдущее значение `value`, которое тоже может быть `undefined`. https://github.com/VKCOM/VKUI/blob/a7b79b3b3a69bdf68163f62521f68697aceb52c2/packages/vkui/src/hooks/useEnsuredControl.ts#L70-L73 В теории (потому что в тестах не удалось повторить), если пользователь в `useCustomEnsuredControl` не которолирует значение `value` (то есть проп `value === undefined`, а мы храним значение `value` локально в `useCustomEnsuredControl`), а затем делает `value` котролируемым (то есть начинает передавать в проп `value` какое-то значение), то есть вероятность, что при вызове `onChanage` `preservedControlledValueRef` ещё не обновился и равен предыдущему значение `value`, то есть `undefined`, тогда пользователь получит `prevValue` со значением `undefined`, что может привести к ошибке, как описано в #7099 > TypeError: Uncaught TypeError: Cannot read properties of undefined (reading 'shown') ## Изменения - Исправили тип `prevValue` в колбэке `nextValue`, заменив `any`, чтобы была видна проблема c undefined. Но теперь ясно, что мы не можем просто так передать `preservedControlledValueRef` в `nextValue` коллбэк в качестве `prevValue`, потому что типы не сходятся. Что можно сделать. 1. Просто не вызывать `nextValue`, если `preservedControlledValueRef` `undefined`, но тогда мы получаем риск не отреагировать на действие пользователя, так как `onChange` не будет вызван и состояние не поменяется. 2. Разрешить `prevValue` по типам быть `undefined`. Но тогда пользователи, использующие в `onChange` коллбэк `nextValue` получат по типам вариант, что `prevValue` может быть `undefined`. Это очень странное состояние и тяжело объяснить его причины. А также тяжело принять решение при обработке onChange. Например, в `useChipsInput` не понятно откуда тогда взять актуальный массив `chips`, чтобы добавить в него новое значение. https://github.com/VKCOM/VKUI/blob/7755cabb003c2dac894703ac30335c0b874d8d52/packages/vkui/src/components/ChipsInput/useChipsInput.ts#L107-L126 3. (Текущее решение) Если `preservedControlledValueRef` `undefined`, то использовать значение `value`. Так как мы знаем, что компонент котролируемый, значит `value` имеет какое-то значение. При переходе из некоторолируемого значения в контролируемое у `value` нету предыдущего состояния, поэтому самым адекватным решением будет предать текущее значение `value` как `prevValue`. Чтобы `value` не находилось в массиве зависимостей `onChange` и не тригерило новое значение `onChange` при изменении, мы `value` прячем в `ref`, и используем лишь тогда, когда `preservedControlledValueRef` === `undefined`. - Добавили warning. Хотелось ещё дать ссылку на [React warning](https://react.dev/reference/react-dom/components/input#im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled), тогда надо пояснять, что в нашем случае речь не только про input, а как это сделать лаконично я не придумал.
Описание
Мы получили репорт об ошибке от пользователей:
shown в объекте используется только в
useFloatingWithInteractions
, который управляется с помощью хукаuseCustomEnsuredControl
.Единственный момент когда бы состояние могло быть
undefined
это момент когда вызывается callbackVKUI/packages/vkui/src/lib/floating/useFloatingWithInteractions/useFloatingWithInteractions.ts
Lines 93 to 102 in 406784a
Тут мы вызываем
setShownLocalState
с функцией, у которой в аргументах может лежать предыдущее состояние стейта.В этот момент у
useCustomEnsuredControl
, если компонент контролируемый и вызываетonChange
с коллбэком, мы обращаемся к переменнойpreservedControlledValueRef
.VKUI/packages/vkui/src/hooks/useEnsuredControl.ts
Lines 70 to 73 in 406784a
Эта переменная может быть
undefined
только при инициализации, до тех пор пока вvalue
не будет передано значение !==undefined
.VKUI/packages/vkui/src/hooks/useEnsuredControl.ts
Lines 49 to 53 in 7d4f104
Изменения
Инициализируем ref сразу же со значением value.
Воспроизведение
Мне никак не удалось довести компонент до состояния ошибки, ни локально, ни в тестовом окружении.
Возможно, что это вообще ложный след.
Возможно, что это также может происходить при изменении состояния компонента из неконтролируемого в контролируемое.
Потому что такая ошибка может возникнуть только если value был
undefined
, потом сталdefined
и был быстро вызванonChange
, так быстро, чтоpreservedControlledValueRef
не успел обновится.Возможно, что стектрейс, который привёл нас сюда не совсем верный. Его надо спрашивать в VK Teams.