Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Perform DOM operation after it was updated inside onMutate in react-query

I wrote a component called Chat (some parts are omitted for simplicity):

function Chat() {
  const containerRef = React.useRef(null);
  const messages = useQuery({
    queryKey: ["messages"],
    queryFn: api.getMessages
  });
  
  const createMessage = useMutation({
    mutationFn: (message) => api.createMessage(message),
    onMutate: (message) => {
      // 1. cancels query, updates cache, returns previous value
      optimisitcUpdate(messages, message);

      // 2. scroll to bottom
      const { current: container } = containerRef;

      // ❌ won't work, because the DOM is not updated yet
      container?.scrollTo(0, container.scrollHeight);

      // 💩 works, because the DOM is updated, but it's hacky
      setTimeout(() => container?.scrollTo(0, container.scrollHeight))
    },
  });

  return (
    <div id="messages" ref={containerRef}>
      {messages.data?.map((message) => (
        <div key={message.uuid}>{message.body}</div>
      ))}
    </div>
  );
}

It’s pretty straightforward. useQuery to fetch all messages, useMutation to create a new message, and return all of the messages.

Whenever createMessage occurs, I want to optimistically update the messages and then scroll to the bottom (like any other chat behavior).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The problem is, I have no way of doing flushSync inside useMutation, so as a workaround, I wrap the logic inside a setTimeout, but that’s a hacky way.

I was wondering if there’s a more robust approach than this.

>Solution :

You can rely on useEffect to execute after the DOM rendering:

useEffect(() => {
  const { current: container } = containerRef;

  container?.scrollTo(0, container.scrollHeight);
)/*, [you may want to put some dependencies here to limit rendering]*/};
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading