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

[Fix] Simplify pendingState type in useActionState #28942

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

yongsk0066
Copy link

@yongsk0066 yongsk0066 commented Apr 28, 2024

Summary

This pull request simplifies the type definition of pendingState in the useActionState hook by changing it from Thenable<boolean> | boolean to just boolean.
The current implementation of useActionState defines the type of pendingState as Thenable<boolean> | boolean. However, upon closer inspection of the code, it appears that pendingState is always set to a boolean value, either directly or via the setPendingState function, which only accepts a boolean argument.

pendingStateHook = mountStateImpl((false: Thenable<boolean> | boolean));
const setPendingState: boolean => void = (dispatchOptimisticSetState.bind(
  null,
  currentlyRenderingFiber,
  false,
  ((pendingStateHook.queue: any): UpdateQueue<
    S | Awaited<S>,
    S | Awaited<S>,
  >),
): any);

In the dispatchActionState function, setPendingState is always called with a boolean value:

setPendingState(true);

Therefore, defining pendingState as Thenable<boolean> | boolean seems unnecessary, as it's never actually set to a thenable value. This pull request proposes to simplify the type to boolean, which more accurately reflects how pendingState is used in practice.
This change improves code clarity and maintainability by removing the potentially confusing Thenable type when it's not needed. It also aligns the type definition with the actual usage of pendingState throughout the useActionState implementation.

Comparison with useTransition

While the code for pendingState in useActionState appears to have been developed with reference to the useTransition hook, it's important to note that these are two different cases that require different handling.

In useTransition, isPending can actually hold a thenable value, as evidenced by this code:

function updateTransition(): [
  boolean,
  (callback: () => void, options?: StartTransitionOptions) => void,
] {
  const [booleanOrThenable] = updateState(false);
  const hook = updateWorkInProgressHook();
  const start = hook.memoizedState;
  const isPending =
    typeof booleanOrThenable === 'boolean'
      ? booleanOrThenable
      : // This will suspend until the async action scope has finished.
        useThenable(booleanOrThenable);
  return [isPending, start];
}

Here, booleanOrThenable is the result of updateState(false), and its type is Thenable<boolean> | boolean. The value of isPending depends on the type of booleanOrThenable:

  • If booleanOrThenable is a boolean,isPending directly uses that value.
  • If booleanOrThenable is a thenable object, isPending is assigned the result of processing the thenable with useThenable.

Therefore, in useTransition, the Thenable<boolean> | boolean type is appropriate for isPending, as it can hold a thenable value.
In contrast, pendingState in useActionState is always used as a boolean value, so we can simplify its type to boolean.

How did you test this change?

To verify that this change doesn't introduce any regressions, I ran the existing test suite with yarn test. All tests passed without any issues, indicating that the updated type definition does not break any existing functionality.

@facebook-github-bot
Copy link

Hi @yongsk0066!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@yongsk0066 yongsk0066 changed the title Fix useActionState PendingState Type Fix pendingState type in useActionState Apr 28, 2024
@yongsk0066 yongsk0066 changed the title Fix pendingState type in useActionState [Fix] Simplify pendingState type in useActionState Apr 28, 2024
@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@react-sizebot
Copy link

Comparing: 190cc99...9da7559

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.66 kB 6.66 kB +0.05% 1.82 kB 1.82 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.67 kB 6.67 kB +0.05% 1.83 kB 1.83 kB
facebook-www/ReactDOM-prod.classic.js = 591.11 kB 591.11 kB = 103.94 kB 103.94 kB
facebook-www/ReactDOM-prod.modern.js = 567.33 kB 567.33 kB = 100.34 kB 100.34 kB
test_utils/ReactAllWarnings.js Deleted 64.26 kB 0.00 kB Deleted 16.02 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
test_utils/ReactAllWarnings.js Deleted 64.26 kB 0.00 kB Deleted 16.02 kB 0.00 kB

Generated by 🚫 dangerJS against 9da7559

@@ -2187,7 +2187,7 @@ function mountActionState<S, P>(

// Pending state. This is used to store the pending state of the action.
// Tracked optimistically, like a transition pending state.
const pendingStateHook = mountStateImpl((false: Thenable<boolean> | boolean));
const pendingStateHook = mountStateImpl((false: boolean));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous code matches how we set pending throughout this file. Why is this the only place where it isn't a thenable?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the desire for consistency, but adding the Thenable type to pendingState when it's not actually used as a Thenable value seems unnecessary. It could make the code harder to understand. I propose we prioritize clarity by matching the type definition to how pendingState is actually used.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it not used like this in the other cases?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of useTransition, a thenable value is indeed passed through chainThenableValue, as shown in the
code :

const thenableForFinishedState = chainThenableValue(
thenable,
finishedState,
);
dispatchSetState(fiber, queue, (thenableForFinishedState: any));

const thenableForFinishedState = chainThenableValue(thenable, finishedState);
dispatchSetState(fiber, queue, (thenableForFinishedState: any));

However, in the useActionState case, specifically in the dispatchActionState function, setPendingState is always called with a boolean value true:

function runActionStateAction<S, P>(
//...
  setPendingState(true);

In useActionState, pendingState is consistently treated as a boolean value, whereas in useTransition, it has the flexibility to be either a boolean or a thenable.

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

Successfully merging this pull request may close these issues.

None yet

4 participants