Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: optimistic state (useOptimistic) shows both optimistic and returned from server data when running several async actions #28574

Open
acherkashin opened this issue Mar 17, 2024 · 6 comments

Comments

@acherkashin
Copy link

acherkashin commented Mar 17, 2024

I have a form with submit button. When I click on the button, I call update optimistic state and update real state as soon as action is finished. When I click on the button 3 times, I will see 6 items in the list for a while.

Issue.Demo.mp4

React version:

  • "react": "18.3.0-canary-bb0944fe5-20240313",
  • "react-dom": "18.3.0-canary-bb0944fe5-20240313"

Steps To Reproduce

  1. Click 3 times on the button

Link to repository:
https://github.com/acherkashin/react19-useOptimistic

Link to sandbox
https://stackblitz.com/~/github.com/acherkashin/react19-useOptimistic

const AddToCartForm = ({ title, addToCart, optimisticAddToCart }: AddToCartFormProps) => {
  const formAction = async (formData: FormData) => {
    const itemId = String(formData.get('itemID'));
    optimisticAddToCart({ id: itemId, title, pending: true });
    try {
      await addToCart(itemId, title);
    } catch (e) {
      console.log(e);
      // show error notification
    }
  };

  return (
    <form action={formAction}>
      <h2>{title}</h2>
      <input type="hidden" name="itemID" value={crypto.randomUUID()} />
      <button type="submit">Add to Cart</button>
    </form>
  );
};

The current behavior

I see every added item 2 times.

The expected behavior

If I add 3 elements, I should see 3 items in the list, not 6.

@acherkashin acherkashin added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Mar 17, 2024
@eps1lon eps1lon added Type: Needs Investigation and removed Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug labels Mar 18, 2024
@eps1lon
Copy link
Collaborator

eps1lon commented Mar 18, 2024

Thank you for the comprehensive repro.

I reduced it a bit more and also added two potential workarounds that I highlighted in comments as "Alternate A" and "Alternate B" as well as one with (in my opinion) degraded UX in "Alternate C": https://stackblitz.com/edit/react-9vvm42?file=src%2FApp.js

First two clicks create just one pending update. Everything works fine.

Last click happens while the action from the prior click is still pending, creating this intermediate state where an item is both in the optimistic and non-optimistic slice.

Screencast.from.18.03.2024.11.01.08.webm

I also added a test that repros the original issue: #28575

However, I'll double check with the team if this shouldn't have "just worked" as you authored it. If either of the workarounds are required, we should definitely document it. Especially since it's not quite clear to me how this would work with primitives e.g. incrementing an optimistic click counter.

@JSerZANP
Copy link

JSerZANP commented Mar 22, 2024

FYI, This could be reproed in the official demo

optimistic.mov

@acherkashin
Copy link
Author

@eps1lon Your "Alternate A" solution is pretty clear and works great.

However, I have a questions about "Alternate B" solution.
Why optimistic update function is called 3 times every time optimistic state is updated?

image

And how this solution work at all, I mean, how item can be inside non-optimistic state earlier optimistic version if we append it to optimistic version first? 🤔

@eps1lon
Copy link
Collaborator

eps1lon commented Mar 22, 2024

Why optimistic update function is called 3 times every time optimistic state is updated?

When an action completes, we rebase the optimistic updates (i.e. replay) on top of the passthrough value (the non-optimistic state). That's why you see multile calls.

@acherkashin
Copy link
Author

@eps1lon thank you for the explanation.

And how this solution work at all, I mean, how item can be inside non-optimistic state earlier optimistic version if we append it to optimistic version first? 🤔

Could you throw the light on the question above?

@eps1lon
Copy link
Collaborator

eps1lon commented Mar 22, 2024

Because we added it to the non-optimistic slice but it's still in the optimistic slice because one action is still pending.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants