-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9933cb5
commit 392b508
Showing
5 changed files
with
130 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,29 @@ | ||
# flushSync | ||
|
||
This example was uses code from | ||
[trellix](https://github.com/remix-run/example-trellix/blob/3379b3d5e9c0173381031e4f062877e8a3696b2e/app/routes/board.%24id/components.tsx). | ||
🧝♂️ I've put together a new component we need. It's called `<EditableText />` and | ||
it allows users to edit a piece of text inline. We display it in a button and | ||
when the user clicks it, the button turns into a text input. When the user | ||
presses enter, blurs, or hits escape, the text input turns back into a button. | ||
|
||
Right now, when the user clicks the button, the button goes away and is replaced | ||
by the text input, but because their focus was on the button which is now gone, | ||
their focus returns to the `<body>` and the text input is not focused. This is | ||
not a good user experience. | ||
|
||
👨💼 Thanks Kellie. So now what we need is for you to properly manage focus for | ||
all of these cases. | ||
|
||
- When the user submits the form (by hitting "enter") | ||
- When the user cancels the form (by hitting "escape") | ||
- When the user blurs the input (by tabbing or clicking away) | ||
|
||
Additionally, when the user clicks the button, we want to select all the text so | ||
it's easy for them to edit. | ||
|
||
🧝♂️ I've added some buttons before and after the input so you have something to | ||
test tab focus with. Good luck! | ||
|
||
<callout-info class="aside"> | ||
This example was uses code from | ||
[trellix](https://github.com/remix-run/example-trellix/blob/3379b3d5e9c0173381031e4f062877e8a3696b2e/app/routes/board.%24id/components.tsx). | ||
</callout-info> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
# flushSync | ||
|
||
👨💼 Awesome job. That's a mighty fine UX! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Focus Management | ||
|
||
👨💼 It's important to consider users of keyboards, both those with disabilities | ||
who rely on them as well as power users who prefer to use the keyboard over the | ||
mouse. Good work! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,96 @@ | ||
# Focus Management | ||
|
||
Helping the user's focus stay on the right place is a key part of the user | ||
experience. This is especially important for users who rely on screen readers or | ||
keyboard navigation. But even able users can benefit from a well-thought focus | ||
management experience. | ||
|
||
Sometimes, the element you want to focus on only becomes available after a state | ||
update. For example: | ||
|
||
```tsx | ||
function MyComponent() { | ||
const inputRef = useRef<HTMLInputElement>(null) | ||
const [show, setShow] = useState(false) | ||
|
||
return ( | ||
<div> | ||
<button onClick={() => setShow(true)}>Show</button> | ||
{show && <input ref={inputRef} />} | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
Presumably after the user clicks "show" they will want to type something in the | ||
input there. Good focus management would focus the input after it becomes | ||
visible. | ||
|
||
It's important for you to know that in React state updates happen in batches. | ||
So state updates do not necessarily take place at the same time you | ||
call the state updater function. | ||
|
||
As a result of React state update batching, if you try to focus an element right | ||
after a state update, it might not work as expected. This is because the element | ||
you want to focus on might not be available yet. | ||
|
||
```tsx remove=10 | ||
function MyComponent() { | ||
const inputRef = useRef<HTMLInputElement>(null) | ||
const [show, setShow] = useState(false) | ||
|
||
return ( | ||
<div> | ||
<button | ||
onClick={() => { | ||
setShow(true) | ||
inputRef.current?.focus() // This probably won't work | ||
}} | ||
> | ||
Show | ||
</button> | ||
{show && <input ref={inputRef} />} | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
The solution to this problem is to force React to run the state and DOM updates | ||
synchronously so that the element you want to focus on is available when you try | ||
to focus it. | ||
|
||
You do this by using the `flushSync` function from the `react-dom` package. | ||
|
||
```tsx | ||
import { flushSync } from 'react-dom' | ||
|
||
function MyComponent() { | ||
const inputRef = useRef<HTMLInputElement>(null) | ||
const [show, setShow] = useState(false) | ||
|
||
return ( | ||
<div> | ||
<button | ||
onClick={() => { | ||
flushSync(() => { | ||
setShow(true) | ||
}) | ||
inputRef.current?.focus() | ||
}} | ||
> | ||
Show | ||
</button> | ||
{show && <input ref={inputRef} />} | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
What `flushSync` does is that it forces React to run the state update and DOM | ||
update synchronously. This way, the input element will be available when you try | ||
to focus it on the line following the `flushSync` call. | ||
|
||
In general you want to avoid this de-optimization, but in some cases (like focus | ||
management), it's the perfect solution. | ||
|
||
Learn more in [the `flushSync` docs](https://react.dev/reference/react-dom/flushSync). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Sync External State |