Commit f8ec772
committed
Fix html.button staying unclickable on Android after toggling disabled back to false (#491)
Closes #491
## Problem
On Android, when an `html.button` (or any RSD element rendered as a `Pressable`) has its `disabled` state set to `true` and then back to `false`, the pressable's own surface remains unclickable — only its children continue to trigger the press handler. On iOS the button recovers correctly.
This does **not** reproduce with a plain React Native `<Pressable disabled={...} />`, and the usual `key`-based remount workaround "fixes" it — both signs that the regression is in how RSD forwards the prop, not in React Native itself.
## Root cause
In `createStrictDOMComponent.js`, the `disabled` prop was forwarded to the underlying `Pressable` **only when it was `true`**, and never reset when it became `false` (`disabled` is intentionally not passed through `useNativeProps`):
```js
if (NativeComponent === ReactNative.Pressable) {
if (props.disabled === true) {
nativeProps.disabled = true;
nativeProps.focusable = false;
}
}
```
So `Pressable` saw `disabled` toggle `true → undefined`, never `true → false`. React Native's `Pressable` only merges `disabled` into its `accessibilityState` when it's non-null:
```js
// react-native/Libraries/Components/Pressable/Pressable.js
_accessibilityState =
disabled != null ? {..._accessibilityState, disabled} : _accessibilityState;
```
With `undefined`, `disabled` drops out, so `accessibilityState.disabled` goes `true → undefined` instead of `true → false`. On Android, `accessibilityState.disabled = true` sets the native view to `enabled = false`; reverting to `undefined` (rather than an explicit `false`) does not re-enable it. A disabled Android `ViewGroup` still dispatches touches to its children (so the inner text kept working) but won't claim touches on its own surface — exactly the reported symptom. iOS has no equivalent native "enabled" latch, so it recovered regardless.
Plain RN `Pressable` works because the user passes an explicit boolean that always resets to `false`.
## Fix
Always forward `disabled` to the `Pressable` as an explicit boolean, so it transitions `true ↔ false` and Android correctly resets the native enabled state:
```js
if (NativeComponent === ReactNative.Pressable) {
// Always pass an explicit boolean so that toggling 'disabled' resets the
// native view's enabled state. Passing 'undefined' when re-enabling leaves
// 'accessibilityState.disabled' unset, which on Android keeps the view's
// native 'enabled' state false and makes the Pressable unclickable.
nativeProps.disabled = props.disabled === true;
if (props.disabled === true) {
nativeProps.focusable = false;
}
}
```
## Note on `focusable` (intentionally left conditional)
`focusable` does **not** need the same symmetric treatment, for two reasons:
1. **It never reaches native as `undefined`.** `Pressable` normalizes it to an explicit boolean on every render via `focusable: focusable !== false`. So when RSD leaves it unset on re-enable, the native View receives `focusable={true}`; when disabled it receives `focusable={false}`. The value always toggles `false ↔ true` at the native layer, so there's no stale-state problem like `disabled` had.
2. **Making it unconditional would regress `tabIndex`.** `focusable` is also derived from `tabIndex` in `useNativeProps` (`nativeProps.focusable = !tabIndex`). The current code only forces `focusable = false` *while disabled* and otherwise preserves the `tabIndex`-derived value. Changing it to something like `focusable = props.disabled !== true` would clobber `tabIndex` (e.g. an enabled button with `tabIndex={-1}` would wrongly become focusable).
So the `disabled` fix alone is sufficient, and the asymmetric `focusable` handling is correct and intentional.
## Tests
- Added a `<button>` regression test asserting that after toggling `disabled` from `true` to `false`, the rendered `Pressable` emits an explicit `disabled={false}` (rather than dropping the prop).
- Full native test suite passes; Flow is clean.1 parent c4e69e0 commit f8ec772
4 files changed
Lines changed: 67 additions & 2 deletions
File tree
- packages/react-strict-dom
- src/native/modules
- tests/html
- __snapshots__
Lines changed: 6 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
93 | 99 | | |
94 | | - | |
95 | | - | |
96 | 100 | | |
97 | 101 | | |
98 | 102 | | |
| |||
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
227 | 227 | | |
228 | 228 | | |
229 | 229 | | |
| 230 | + | |
230 | 231 | | |
231 | 232 | | |
232 | 233 | | |
| |||
345 | 346 | | |
346 | 347 | | |
347 | 348 | | |
| 349 | + | |
348 | 350 | | |
349 | 351 | | |
350 | 352 | | |
| |||
812 | 814 | | |
813 | 815 | | |
814 | 816 | | |
| 817 | + | |
815 | 818 | | |
816 | 819 | | |
817 | 820 | | |
| |||
965 | 968 | | |
966 | 969 | | |
967 | 970 | | |
| 971 | + | |
968 | 972 | | |
969 | 973 | | |
970 | 974 | | |
| |||
979 | 983 | | |
980 | 984 | | |
981 | 985 | | |
| 986 | + | |
982 | 987 | | |
983 | 988 | | |
984 | 989 | | |
| |||
1037 | 1042 | | |
1038 | 1043 | | |
1039 | 1044 | | |
| 1045 | + | |
1040 | 1046 | | |
1041 | 1047 | | |
1042 | 1048 | | |
| |||
1068 | 1074 | | |
1069 | 1075 | | |
1070 | 1076 | | |
| 1077 | + | |
1071 | 1078 | | |
1072 | 1079 | | |
1073 | 1080 | | |
| |||
1426 | 1433 | | |
1427 | 1434 | | |
1428 | 1435 | | |
| 1436 | + | |
1429 | 1437 | | |
1430 | 1438 | | |
1431 | 1439 | | |
| |||
1663 | 1671 | | |
1664 | 1672 | | |
1665 | 1673 | | |
| 1674 | + | |
1666 | 1675 | | |
1667 | 1676 | | |
1668 | 1677 | | |
| |||
1781 | 1790 | | |
1782 | 1791 | | |
1783 | 1792 | | |
| 1793 | + | |
1784 | 1794 | | |
1785 | 1795 | | |
1786 | 1796 | | |
| |||
1899 | 1909 | | |
1900 | 1910 | | |
1901 | 1911 | | |
| 1912 | + | |
1902 | 1913 | | |
1903 | 1914 | | |
1904 | 1915 | | |
| |||
2775 | 2786 | | |
2776 | 2787 | | |
2777 | 2788 | | |
| 2789 | + | |
2778 | 2790 | | |
2779 | 2791 | | |
2780 | 2792 | | |
| |||
2900 | 2912 | | |
2901 | 2913 | | |
2902 | 2914 | | |
| 2915 | + | |
2903 | 2916 | | |
2904 | 2917 | | |
2905 | 2918 | | |
| |||
3805 | 3818 | | |
3806 | 3819 | | |
3807 | 3820 | | |
| 3821 | + | |
3808 | 3822 | | |
3809 | 3823 | | |
3810 | 3824 | | |
| |||
4046 | 4060 | | |
4047 | 4061 | | |
4048 | 4062 | | |
| 4063 | + | |
4049 | 4064 | | |
4050 | 4065 | | |
4051 | 4066 | | |
| |||
4166 | 4181 | | |
4167 | 4182 | | |
4168 | 4183 | | |
| 4184 | + | |
4169 | 4185 | | |
4170 | 4186 | | |
4171 | 4187 | | |
| |||
4298 | 4314 | | |
4299 | 4315 | | |
4300 | 4316 | | |
| 4317 | + | |
4301 | 4318 | | |
4302 | 4319 | | |
4303 | 4320 | | |
| |||
4897 | 4914 | | |
4898 | 4915 | | |
4899 | 4916 | | |
| 4917 | + | |
4900 | 4918 | | |
4901 | 4919 | | |
4902 | 4920 | | |
| |||
5027 | 5045 | | |
5028 | 5046 | | |
5029 | 5047 | | |
| 5048 | + | |
5030 | 5049 | | |
5031 | 5050 | | |
5032 | 5051 | | |
| |||
5888 | 5907 | | |
5889 | 5908 | | |
5890 | 5909 | | |
| 5910 | + | |
5891 | 5911 | | |
5892 | 5912 | | |
5893 | 5913 | | |
| |||
Lines changed: 16 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
216 | 216 | | |
217 | 217 | | |
218 | 218 | | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
219 | 235 | | |
220 | 236 | | |
221 | 237 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
500 | 500 | | |
501 | 501 | | |
502 | 502 | | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
503 | 528 | | |
504 | 529 | | |
505 | 530 | | |
| |||
0 commit comments