Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[internal] Remove unneededuseEventCallback usage#2917

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

atomiks
Copy link
Contributor

@atomiksatomiks commentedOct 4, 2025
edited
Loading

After React 19.2 introduceduseEffectEvent I decided to investigate why we're usinguseEventCallback (which is a broader version of it without linting restrictions) to see how we could migrate to it or why the React team chose to keep it restricted.

Firstly, I removed most instances where it's truly unnecessary as no memoization is occurring.

Secondly, I found various use cases where it directly matchesuseEffectEvent's intended purpose — an event handler that's invoked inside an effect. Or in one special case, used to stabilize a function used as a dependency of an effect.

The use cases I found that are broader than the hook's intended purpose:

  1. Using a callback ref where specifying the dependencies is unnecessary, especially for complex ref callbacks that don't need reactivity
  2. Reusable event handlers that are forwarded through contexts or memoized groups of props where memoization is a must - nothing that's called during render and needs reactivity
  3. A tiny DX improvement where it just turns anundefined value into a NOOP function

We also haveuseLatestRef, which is a non-function version of making an "untracked" helper. Most other libraries provide this built-in:https://x.com/rickhanlonii/status/1974118493185540588.

If the React team weren't wary of wholesale turning off reactivity for something (other libraries provide this already),useEffectEvent could've been calleduseUntracked to have the broadest set of use cases.

useEffectEvent also allows you to access non-reactive non-function values since it respects return values:

constexternalProp={};constgetProp=React.useEffectEvent(()=>externalProp);React.useEffect(()=>{constprop=getProp();},[]);

AuseUntracked helper that handles all non-reactive values simultaneously, merginguseLatestRef anduseEventCallback together, needs an overloaded return value, which is probably not as neat as just renaminguseEventCallback to something similar touseEffectEvent but allowing us to bypass its restrictive lint rule to solve our broader use cases (outlined above in the ordered list)

@romgrk

'use client';import{useEventCallback}from'./useEventCallback';import{useLatestRef}from'./useLatestRef';typeCallback=(...args:any[])=>any;/** * Non-reactive storage for any value. * - If `value` is a function, returns a stable callable that always uses the latest implementation. * - Otherwise, returns a `RefObject` object whose `.current` is kept in sync with the latest value. */exportfunctionuseUntracked<TextendsCallback>(value:T|undefined|null):T;exportfunctionuseUntracked<T>(value:T):React.RefObject<T>;exportfunctionuseUntracked(value:unknown):unknown{constisFunction=typeofvalue==='function';constcallable=useEventCallback(isFunction ?value :undefined);constlatestRef=useLatestRef(isFunction ?undefined :value);returnisFunction ?callable :latestRef;}

IfuseUntrackedonly returned function values and respected return values, that could work nicely. We could removeuseLatestRef as a utility in this case.

@atomiksatomiks added the internalBehind-the-scenes enhancement. Formerly called “core”. labelOct 4, 2025
@pkg-pr-newpkg.pr.new
Copy link

pkg-pr-newbot commentedOct 4, 2025
edited
Loading

vite-css-base-ui-example

pnpm add https://pkg.pr.new/mui/base-ui/@base-ui-components/react@2917
pnpm add https://pkg.pr.new/mui/base-ui/@base-ui-components/utils@2917

commit:b1e61d1

@netlifyNetlify
Copy link

netlifybot commentedOct 4, 2025
edited
Loading

Deploy Preview forbase-ui ready!

NameLink
🔨 Latest commitb1e61d1
🔍 Latest deploy loghttps://app.netlify.com/projects/base-ui/deploys/68e61f968216b00008a48865
😎 Deploy Previewhttps://deploy-preview-2917--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to yourNetlify project configuration.

@mui-bot
Copy link

mui-bot commentedOct 4, 2025
edited
Loading

Bundle size report

BundleParsed sizeGzip size
@base-ui-components/react▼-116B(-0.03%)▼-36B(-0.03%)

Details of bundle changes

@atomiksatomiksforce-pushed therefactor/remove-use-event-callback branch fromae1f9f5 to79b332dCompareOctober 4, 2025 06:29
@atomiksatomiksforce-pushed therefactor/remove-use-event-callback branch from79b332d to2da7ddfCompareOctober 4, 2025 06:32
@github-actionsgithub-actionsbot added the PR: out-of-dateThe pull request has merge conflicts and can't be merged. labelOct 6, 2025
Comment on lines -16 to +18
const{ children, elementsRef, labelsRef, onMapChange}=props;
const{ children, elementsRef, labelsRef,onMapChange:onMapChangeProp}=props;

constonMapChange=useEventCallback(onMapChangeProp);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this change beneficial?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

It aligns with what theuseEffectEvent linter wants

Comment on lines -78 to +83
constregisterControlRef=useEventCallback((element:HTMLButtonElement|null)=>{

constregisterControlRef=React.useCallback((element:HTMLButtonElement|null)=>{
if(controlRef.current==null&&element!=null&&!element.hasAttribute(PARENT_CHECKBOX)){
controlRef.current=element;
}
});
},[]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this changed?useEventCallback makes the intent more clear and is more performant.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

@michaldudak wrote thatuseCallback can be more performant

#2336 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

It makes sense now that I re-read the source code,useEventCallback has to deal with init/render cycles. We could add something likeuseUntrackedCallback (name tbd) as a version ofReact.useCallback for functions without tracked dependencies like this one.

Comment on lines -25 to +27
constreset=useEventCallback(()=>{
constreset=React.useCallback(()=>{
setOpenMethod(null);
});
},[]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Idem, why move touseCallback?

@romgrk
Copy link
Contributor

to see how we could migrate to it or why the React team chose to keep it restricted

The React team often makes decisions that align mostly with Meta's internal needs, and they've stopped putting much effort & thought into the external DX (which the react-hooks eslint package is part of). I wouldn't read too much into why they took that decision.

A useUntracked helper that handles all non-reactive values simultaneously, merging useLatestRef and useEventCallback together

The code example you listed does runtime type detection and calls bothuseEventCallback anduseLatestRef each time it's used - this is unefficient. Calling react hooks is not a free operation, there is a runtime cost to be paid. And usually, runtime type detection is a code smell. There's two different use-cases, there is no need to merge them in a single hook. At most, we could rename them to highlight their similarities in behavior, but merging them muddles their type signature and return valule (refs & callbacks are fairly different types of values) and affects performance.

Dakkers reacted with thumbs up emoji

@atomiks
Copy link
ContributorAuthor

@romgrk yeah the overload isn't great, which is why I wrote aboutuseEffectEvent being able to return values.

What do you think about renaminguseEventCallback touseUntracked to better indicate it can return values and be used for that case overuseLatestRef? It will still only return functions, but you can call them inside effects (similar to accessing refs)

constcallback=useUntracked(()=>{});constgetValue=useUntracked(()=>prop);

Signed-off-by: atomiks <cc.glows@gmail.com>
@github-actionsgithub-actionsbot removed the PR: out-of-dateThe pull request has merge conflicts and can't be merged. labelOct 7, 2025
@romgrk
Copy link
Contributor

What do you think about renaminguseEventCallback touseUntracked

I think that name is confusing. The "callback" token is fairly important semantically. I would instead suggestuseLatestCallback, though I'm not sure renaming it is worth it.

@atomiks
Copy link
ContributorAuthor

atomiks commentedOct 7, 2025
edited
Loading

@romgrk it's the term used to turn off reactivity in every other library (Svelte, Solid, Preact, Angular, Vue, ...).
Here's a big thread with them all:https://x.com/rickhanlonii/status/1974118493185540588

It's just that returning values fromuseEffectEvent (oruseEventCallback) seems weird as they seem like they should just fire side-effects alone. I was never a big fan of the term "latest", though.

The "callback" token is fairly important semantically

Somewhat, butuseEffectEvent returns a function even though an "event" is typically an object, so I guess it's not critical. I could live withuseUntrackedCallback but I like shorter names (and since it only ever accepts a function as a param, it seems unnecessary as the return value matches the input. It's intuitive that callinguseUntracked will untrack the function passed).

@atomiksatomiks marked this pull request as ready for reviewOctober 7, 2025 05:22
@github-actionsgithub-actionsbot added the PR: out-of-dateThe pull request has merge conflicts and can't be merged. labelOct 7, 2025
@github-actionsgithub-actionsbot removed the PR: out-of-dateThe pull request has merge conflicts and can't be merged. labelOct 8, 2025
Copy link
Member

@mnajdovamnajdova left a comment

Choose a reason for hiding this comment

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

Can we document theuseEffectEvent and explain when it should be used? To have at least some kind of guidelines for other contirbutors.

@atomiks
Copy link
ContributorAuthor

Can we document the useEffectEvent and explain when it should be used? To have at least some kind of guidelines for other contirbutors.

I was going to do this in a new PR. This one doesn't create any new hooks/utils yet, just cleans up the code.

mnajdova reacted with thumbs up emoji

Copy link
Member

@mnajdovamnajdova left a comment

Choose a reason for hiding this comment

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

Changes make sense 👌 I haven't check the whole codebase to find other occurences, but if there is anything missed we can potentially fix those afterwards.

@atomiksatomiks merged commit1f62103 intomui:masterOct 8, 2025
20 checks passed
@atomiksatomiks deleted the refactor/remove-use-event-callback branchOctober 8, 2025 09:47
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@romgrkromgrkromgrk left review comments

@mnajdovamnajdovamnajdova approved these changes

@colmtuitecolmtuiteAwaiting requested review from colmtuitecolmtuite is a code owner

@michaldudakmichaldudakAwaiting requested review from michaldudakmichaldudak is a code owner

Assignees

No one assigned

Labels

internalBehind-the-scenes enhancement. Formerly called “core”.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

4 participants

@atomiks@mui-bot@romgrk@mnajdova

[8]ページ先頭

©2009-2025 Movatter.jp