From 392b5084ec1c2c4c0b7b76dedb27a8aedb30556c Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Fri, 8 Mar 2024 18:09:45 -0700 Subject: [PATCH] finish exercise 8 --- .../08.focus/01.problem.flush-sync/README.mdx | 29 +++++- .../01.solution.flush-sync/README.mdx | 2 + exercises/08.focus/FINISHED.mdx | 5 + exercises/08.focus/README.mdx | 95 +++++++++++++++++++ exercises/09.sync-external/FINISHED.mdx | 1 + 5 files changed, 130 insertions(+), 2 deletions(-) diff --git a/exercises/08.focus/01.problem.flush-sync/README.mdx b/exercises/08.focus/01.problem.flush-sync/README.mdx index 259ef8b8..70acebb3 100644 --- a/exercises/08.focus/01.problem.flush-sync/README.mdx +++ b/exercises/08.focus/01.problem.flush-sync/README.mdx @@ -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 `` 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 `` 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! + + + This example was uses code from + [trellix](https://github.com/remix-run/example-trellix/blob/3379b3d5e9c0173381031e4f062877e8a3696b2e/app/routes/board.%24id/components.tsx). + diff --git a/exercises/08.focus/01.solution.flush-sync/README.mdx b/exercises/08.focus/01.solution.flush-sync/README.mdx index c83fb3ca..c5e36504 100644 --- a/exercises/08.focus/01.solution.flush-sync/README.mdx +++ b/exercises/08.focus/01.solution.flush-sync/README.mdx @@ -1 +1,3 @@ # flushSync + +👨‍💼 Awesome job. That's a mighty fine UX! diff --git a/exercises/08.focus/FINISHED.mdx b/exercises/08.focus/FINISHED.mdx index e69de29b..4a4940a0 100644 --- a/exercises/08.focus/FINISHED.mdx +++ b/exercises/08.focus/FINISHED.mdx @@ -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! diff --git a/exercises/08.focus/README.mdx b/exercises/08.focus/README.mdx index de8a89a6..a410a58a 100644 --- a/exercises/08.focus/README.mdx +++ b/exercises/08.focus/README.mdx @@ -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(null) + const [show, setShow] = useState(false) + + return ( +
+ + {show && } +
+ ) +} +``` + +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(null) + const [show, setShow] = useState(false) + + return ( +
+ + {show && } +
+ ) +} +``` + +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(null) + const [show, setShow] = useState(false) + + return ( +
+ + {show && } +
+ ) +} +``` + +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). diff --git a/exercises/09.sync-external/FINISHED.mdx b/exercises/09.sync-external/FINISHED.mdx index e69de29b..0243852c 100644 --- a/exercises/09.sync-external/FINISHED.mdx +++ b/exercises/09.sync-external/FINISHED.mdx @@ -0,0 +1 @@ +# Sync External State