|
| 1 | +- Start Date: 2023-07-19 |
| 2 | +- RFC PR: (leave this empty) |
| 3 | +- React Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +This proposal suggests simplifying the usage of `startTransition` in `React` by |
| 8 | +introducing a new hook. |
| 9 | + |
| 10 | +# Basic example |
| 11 | + |
| 12 | +Currently, in order to utilize `startTransition`, developers need to |
| 13 | +import `useTransition` and set it up as follows: |
| 14 | + |
| 15 | +```js |
| 16 | +const [isPending, startTransition] = useTransition(); |
| 17 | +``` |
| 18 | + |
| 19 | +However, in some cases, the `isPending` value is not used, but it still needs to be included because `startTransition` occupies the second index of the `useTransition` output. Additionally, `startTransition` often needs to work in conjunction with `useState`. |
| 20 | + |
| 21 | +To address these complexities, I propose combining these hooks into a new one named `useDeferredState` |
| 22 | + |
| 23 | +```js |
| 24 | +const [text, setText, isPending] = useDeferredState(); |
| 25 | +``` |
| 26 | + |
| 27 | +Alternatively, we can achieve the same result using a HOF to wrap `useState`, like this: |
| 28 | + |
| 29 | +```js |
| 30 | +const [text, setText, isPending] = deferred(useState()); |
| 31 | +``` |
| 32 | + |
| 33 | +# Motivation |
| 34 | +The main motivation behind this proposal is to streamline the usage of `useTransition` and `startTransition` in `React`, making it more straightforward and intuitive for developers. By introducing a new hook or a HOF, we can enhance the developer experience and promote cleaner, more concise code when working with transitions in React. |
| 35 | + |
| 36 | +# Detailed design |
| 37 | +`useDeferredState` and `deferred` are a combination of two main React hooks `useState` and `useTransition`. |
| 38 | + |
| 39 | +By placing `isPending` as the third element in the output, it provides the flexibility to choose whether or not to utilize it in your code: |
| 40 | +```js |
| 41 | +// Use 'isPending' when needed: |
| 42 | +const [text, setText, isPending] = useDeferredState(); |
| 43 | + |
| 44 | +// Omit 'isPending' when not required: |
| 45 | +const [text, setText] = useDeferredState(); |
| 46 | +``` |
| 47 | + |
| 48 | +# Drawbacks |
| 49 | + |
| 50 | +- I think teaching `useTransition` is much harder than teaching `useDeferredState` |
| 51 | +- There is no breaking chaneg therefore there is no cost of migrating existing React applications. |
| 52 | + |
| 53 | +# Alternatives |
| 54 | + |
| 55 | +An alternative solution is having `useDeferredState` or `deferred` as |
| 56 | +a 3th-party package. |
| 57 | + |
| 58 | +```js |
| 59 | +const useDeferredState = (initialValue) => { |
| 60 | + const [state, _setState] = useState(initialValue); |
| 61 | + const [isPenfing, startTransition] = useTransition(); |
| 62 | + |
| 63 | + const setState = useCallback((newValue) => { |
| 64 | + startTransition(() => { |
| 65 | + _setState(newValue); |
| 66 | + }); |
| 67 | + }, []); |
| 68 | + |
| 69 | + return [state, setState, isPenfing]; |
| 70 | +} |
| 71 | + |
| 72 | +// usage: |
| 73 | +const [text, setText, isPending] = useDeferredState(); |
| 74 | +``` |
| 75 | + |
| 76 | +or |
| 77 | + |
| 78 | +```js |
| 79 | +const deferred = ([state, _setState]) => { |
| 80 | + const [isPenfing, startTransition] = useTransition(); |
| 81 | + |
| 82 | + const setState = useCallback((newValue) => { |
| 83 | + startTransition(() => { |
| 84 | + _setState(newValue); |
| 85 | + }); |
| 86 | + }, []); |
| 87 | + |
| 88 | + return [state, setState, isPenfing]; |
| 89 | +} |
| 90 | + |
| 91 | +// usage: |
| 92 | +const [text, setText, isPending] = deferred(useState()); |
| 93 | +``` |
| 94 | + |
| 95 | +# Adoption strategy |
| 96 | + |
| 97 | +The proposed hook and Higher-Order Function are opt-in features, meaning that developers can |
| 98 | +choose whether or not to use them in their code. Existing React applications that don't require |
| 99 | +the new functionalities can continue using the traditional `useState` and `useTransition` approaches |
| 100 | +without any changes. |
| 101 | + |
| 102 | +# How we teach this |
| 103 | + |
| 104 | +`useDeferredState`: |
| 105 | +The proposed hook combines `useState` with a deferred state update mechanism to handle non-urgent updates. The term `deferred` accurately describes the behavior of the state update, as it delays execution until a later time (when React is less busy). This name aligns with React's existing hooks, such as `useEffect`, which also involve deferred operations. |
| 106 | + |
| 107 | +`deferred`: |
| 108 | +When introducing the Higher-Order Function, it wraps useState to achieve similar functionality to the `useDeferredState` hook. The name `deferred` directly reflects the primary purpose of the function, emphasizing its role in handling state updates in a deferred manner. |
| 109 | + |
| 110 | +# Unresolved questions |
| 111 | +Is there any downside if we use multiple `useTransition` in a component? |
0 commit comments