An Interest In:
Web News this Week
- April 15, 2024
- April 14, 2024
- April 13, 2024
- April 12, 2024
- April 11, 2024
- April 10, 2024
- April 9, 2024
Have you used `flushSync` in React?
In this post we'll discuss about the flushSync
utility provided by react-dom
.
Let's try and understand what flushSync
is and how it can useful through an example.
As always, it's a simple todo example but the point to note here is that the todo container has fixed height and is scrollable.
So, there's our App
component that has a todos
state and returns a list of todos along with a form.
export default function App() { const [todos, setTodos] = useState(mockTodos); const onAdd = (newTask) => { setTodos([...todos, { id: uuid(), task: newTask }]); }; return ( <section className="app"> <h1>Todos</h1> <ul style={{ height: 200, overflowY: "auto" }}> {todos.map((todo) => ( <li key={todo.id}>{todo.task}</li> ))} </ul> <AddTodo onAdd={onAdd} /> </section> );}
The AddTodo
component is also fairly simple, it just manages the input state and once the form is submitted it calls the onAdd
prop with the new todo.
const AddTodo = ({ onAdd }) => { const [taskInput, setTaskInput] = useState(""); const handleSubmit = (e) => { e.preventDefault(); if (!taskInput.trim()) return; setTaskInput(""); onAdd(taskInput.trim()); }; return ( <form onSubmit={handleSubmit}> <input type="text" placeholder="Your Task" value={taskInput} onChange={(e) => setTaskInput(e.target.value)} /> <button>Add Task</button> </form> );};
Now that we've an understanding of how our code works, suppose we want to add a functionality where every time a new todo is added, the container is scrolled to its bottom so that the newly added todo is visible to the user.
Think for while and figure out how you would go about implementing this functionality.
Using useEffect
hook
So, you might be thinking of using the effect hook. Every time the todos
change just scroll the container to the bottom.
useEffect(() => { listRef.current.scrollTop = listRef.current.scrollHeight; // listRef is simply a ref attached to the ul}, [todos]);
OR
useEffect(() => { const lastTodo = listRef.current.lastElementChild; lastTodo.scrollIntoView();}, [todos]);
Both of these work fine but you might want to you use the useLayoutEffect
hook in this situation in case you observe any jitters in scrolling.
But, I would not want to put this in either of these hooks because I feel that more than a side effect, it actually should be a part of the onAdd
handler itself. So, let's try and do that.
Scrolling logic inside the handler
If you simply put the scrolling logic inside the handler (as shown below), you would notice that you're not exactly getting the desired results.
const onAdd = (newTask) => { setTodos([...todos, { id: uuid(), task: newTask }]); const lastTodo = listRef.current.lastElementChild; lastTodo.scrollIntoView();};
Because setTodos
is not synchronous, what happens is you scroll first and then the todos
actually get updated. So, what's in view is not the last todo but second to last.
So, to get it working as expected we would have to make sure that the logic for scrolling runs only after the todos
state has been updated. And that's where flushSync
comes handy.
Using flushSync
To use flushSync
, we need to import it from react-dom
: import { flushSync } from "react-dom";
And now we can wrap the setTodos
call inside flushSync
handler (as shown below).
const onAdd = (newTask) => { flushSync(() => { setTodos([...todos, { id: uuid(), task: newTask }]); }); const lastTodo = listRef.current.lastElementChild; lastTodo.scrollIntoView();};
Now we have made sure that the state update happens synchronously and the logic for scrolling is executed only after the state has been updated.
That's it for this post, let me know situations where you would want to use flushSync
.
Peace
Original Link: https://dev.to/ssmkhrj/have-you-used-flushsync-in-react-4cpo
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To