react icon indicating copy to clipboard operation
react copied to clipboard

[React 19] useOptimistic shows wrong value when other actions happen in the background

Open denis-sokolov opened this issue 1 year ago • 3 comments

Summary

Any unrelated background action blocks useOptimistic’s heuristic as to when to revert to the real value.

Reproduce

  1. Open the demo

    Code backup
    import { useOptimistic, useState } from "react";
    
    const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    
    export default function App() {
      const [a, setA] = useState(0);
      const [optimisticA, setOptimisticA] = useOptimistic(a);
      async function incA() {
        const newVal = optimisticA + 1;
        setOptimisticA(newVal);
        await sleep(1000);
        return Error("failed");
      }
    
      async function unrelatedSlow() {
        await sleep(20000);
      }
    
      return (
        <div className="App">
          <div>
            <form action={incA}>
              {optimisticA} <button disabled={optimisticA !== a}>Inc</button>
            </form>
          </div>
          <div>
            <form action={unrelatedSlow}>
              <button>Unrelated slow</button>{" "}
            </form>
          </div>
        </div>
      );
    }
    
    
  2. Click on Inc.

  3. Observe how after 1 second and a simulated server failure the number goes back to 0.

  4. Click on Unrelated slow.

  5. Click on Inc.

Expected: after 1 second number goes back to 0, same as before. Actual: the number stays at 1 for 20 seconds, until the unrelated action completes.

denis-sokolov avatar Aug 08 '24 14:08 denis-sokolov

Personally, I am not sure how the current design of useOptimistic should be able to work.

In the general case, when building optimistic UI, every change may need to be rebased or reverted. The only way to achieve that is for the changes to be expressed as pure functions. (Perhaps the second parameter to useOptimistic is supposed to be required, and then #30638 is the answer to my question)

Also, in the general case when building optimistic UI, every change must be tied to the success and failure. As in we need to know which change to rollback and which to replay. The current design seems to be fundamentally unable to do that, and thus more complex state changes might never play well with concurrent operations. (Perhaps this is the cause of #28574)

This is not a theoretical concern. Multiple concurrent optimistic operations are absolutely the norm in a complex local-first application.

denis-sokolov avatar Aug 08 '24 14:08 denis-sokolov

@denis-sokolov I can work on this issue can you please assign me this issue

Vivek-1102 avatar Oct 08 '24 12:10 Vivek-1102

@denis-sokolov I'd like to help with this issue. Could you please assign it to me?

vidyabhandari avatar Oct 11 '24 14:10 vidyabhandari

  1. Click on Inc.
  2. Observe how after 1 second and a simulated server failure the number goes back to 0.
  3. Click on Unrelated slow.
  4. Click on Inc.

Don't you think it's strange that even if you don't return an error object, it still returns a value? I forked your example, removed the error object and it still returns 0 even without an error

I may not understand something and this api is new to me, but it shouldn't work like this https://github.com/facebook/react/issues/31967

MrOxMasTer avatar Jan 03 '25 06:01 MrOxMasTer

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Jun 09 '25 21:06 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

github-actions[bot] avatar Jun 16 '25 22:06 github-actions[bot]

Updated with the newest React version and issue details and reopened as #33545.

denis-sokolov avatar Jun 16 '25 23:06 denis-sokolov