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

RFC: useEvent #220

Closed
wants to merge 2 commits into from
Closed

RFC: useEvent #220

wants to merge 2 commits into from

Conversation

gaearon
Copy link
Member

@gaearon gaearon commented May 4, 2022

In this RFC, we propose a useEvent Hook. It lets you define event handlers that can read the latest props/state but have always stable function identity. Event handlers defined with useEvent don't break memoization and don't retrigger effects.

View formatted RFC

FAQ:


Update (Sep 2022): we're planning to revise this proposal (and possibly rename it)

@windmaomao
Copy link

Finally something easy to think of is implemented as it seems. 👍🏻

@ds300
Copy link

ds300 commented May 4, 2022

Great idea ❤️

My main suggestion would be a different name. useEvent is not a very precise way to convey what this hook does. I've seen it implemented in other codebases as useStableCallback, which I slightly prefer, although I see that you've already dismissed it as being too verbose in the RFC.

@Merri
Copy link

Merri commented May 4, 2022

Or maybe just name this useCallback and make it work for both cases, but deprecate the passing of dependencies and remove some version later.

Unless someone can figure out any valid use case for the old useCallback functionality with deps.

@windmaomao
Copy link

As for the name, IMHO, react hook is never named for tech purpose, more in the business direction. If i really want to be picky, i can think of useGlobal or useStatic.

@panta82
Copy link

panta82 commented May 4, 2022

I've been using this all over the place. I call it useLatestClosure. It's definitely the missing piece of React.

I don't like useEvent naming. IMO using these as events will be a very minor part of usage pattern, just an optimization in the largest codebases. Mostly it will be used when you need to pass a function somewhere where you don't want to trigger re-render.

Give the useEvent behavior to useCallback. We don’t want to do this because they have sufficiently different semantics.

I think this would be a better change. I almost always want to use useLatestClosure (or useEvent) and almost never useCallback with an argument list.

As Merri said, useCallback should just adopt this functionality when called without deps. And deps usage should be an advanced optimization use case.

@nikparo
Copy link

nikparo commented May 4, 2022

Great to see a proposal for this! I'm left wondering though what type of footguns you have identified related to using event functions during render? Out of date callbacks?

Same proposal, but allow calling event handlers during rendering. We think this creates too many footguns.

If calling event handlers during rendering was allowed, then would the event handler be up to date or not? I.e. would isCurrent in the example below always be true? If that is a possibility, then I think it warrants serious consideration.

  const [value, setValue] = useState(0);
  const getCurrentValue = useEvent(() => value);

  // always true or sometimes false?
  const isCurrent = value === getCurrentValue();

@kocyigityunus
Copy link

kocyigityunus commented May 4, 2022

I didn't like the useEvent naming too. maybe split it into two different hooks.

  1. useChange => with dependencies, since it re-runs when one of the dependencies changes.
  2. useFunction or useFunctionRef => without dependencies. since it's basically useRef for a function.

@franciscop
Copy link

franciscop commented May 4, 2022

To be implementation-clear, the behaviour proposed here is not the same as this, right?

// NOT the proposal? Since it would have stale props
const useEvent = (cb) => {
  const wrapper = useCallback(cb, []);
  return wrapper;
};

Nevertheless, text inside useEvent will reflect its latest value

It'd be more like this (+some err handling), which means the wrapper is always stable but inside keeps the latest ref to the callback and calls that when invoked:

const useEvent = (cb) => {
  const ref = useRef(cb);
  useEffect(() => {
    ref.current = cb;
  }, [cb]);
  const wrapper = useCallback((...args) => {
    ref.current(...args);
  }, []);
  return wrapper;
};

@alvarlagerlof
Copy link

Please Keep the name. It's far less abstract than the other proposals, which will help a lot of non-native speakers.

@rubennorte
Copy link

@franciscop there's an example of the internal implementation of this hook in the proposal: https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md#internal-implementation

@franciscop
Copy link

franciscop commented May 4, 2022

What happens when the event is asynchronous?

This is a big usecase IMHO and a big pain point in current React's useEffect where you cannot do useEffect(async () => {...});. I see little mention of this very typical usecase (only "events should usually not be asynchronous"), what happens here? Is this valid?

const onClick = useEvent(async e => {
  ...
});

I think for a Hook that is going to be used in a highly-async environment, it'd be nice to have some async help in-place. Like, at the very least allow the callback to be async by design.

Disclaimer: I've written part of use-async-effect and all of use-async 😬

@rubennorte
Copy link

rubennorte commented May 4, 2022

Sorry to continue with the bikeshedding on the name of the hook, but I think my perspective is slightly different from what has been shared already.

I think the concept of "event" aligns well with what these functions are intended for. Given they're not meant to be used during render (they can only be used when the tree has been committed) the only moment when they can actually execute is during an event (a user interaction event, a network event, a timer event, etc.).

My only concern is about calling it just useEvent because the name might suggest this is to subscribe to events, like:

useEvent('click', () => {
  // do something
});

Which isn't what it does.

In order to avoid this confusion, I think we should use something like useEventHandler or useEventCallback.

@so1ua
Copy link

so1ua commented May 4, 2022

useFunction is better :) Especially that as I understand useCallback will almost not be used later

@rubennorte
Copy link

rubennorte commented May 4, 2022

What happens when the event is asynchronous?

This is a big usecase IMHO and a big pain point in current React's useEffect where you cannot do useEffect(async () => {...});. I see little mention of this very typical usecase (only "events should usually not be asynchronous"), what happens here? Is this valid?

const onClick = useEvent(async e => {
  ...
});

I think for a Hook that is going to be used in a highly-async environment, it'd be nice to have some async help in-place. Like, at the very least allow the callback to be async by design.

Disclaimer: I've written part of use-async-effect and all of use-async 😬

@franciscop what kind of help are you thinking about here? What do you think React should do differently when passing an async function instead of a sync one, considering React just returns a stable version of the function but doesn't really call that function itself?

@ScottAwesome
Copy link

First and foremost, this solves a huge pain point for the community. I love it so much. The over proliferation of useCallback and associated bugs are solved pretty concisely in this addition.

In terms of naming: I think you could call it useEventHandler. I see the purpose of useCallback being useful for things where you need data invalidation to change the function signature, like when interacting with results from an API. I does become more of an edge case tool but that's kind of the point? Arguably, one could just use useMemo for complex cases too, but the simplicity of useCallback for non event handling cases is nice.

Perhaps clarify guidance around async functions, e.g. if you are using an async action it might be better to treat it as a Promise rather than using async and await?

@franciscop
Copy link

franciscop commented May 4, 2022

I'm thinking to at least allow the function to be async, since if it's async in useEffect even when the return value is not used officially it'll give you a strong warning an error. Like, as far as it doesn't get in the way of that I'd be happy already.

What I was thinking as active help is that if this function (which doesn't seem to be the case from the RFC, but the name is slightly confusing) was supposed to be to manage only events like onClick it could also help by calling event.persist() by default. Though I'm just reading that it seems that event.persist() is useless since React 17 and I didn't know that, so there might be nothing else to do here!

Edit: I should re-read the docs more often, React keeps getting better in small ways (not needing e.persist()) and I didn't even notice.

@sompylasar
Copy link

I'd also argue strongly against useEvent name because an "event" is something that happens, but what we're making with this hook is a callback function which is supposed to handle/process the happened event. Suggest more specific useEventCallback or useEventHandler instead, even though it's longer to type. I would avoid going as abstract as useLatestClosure or useFunction.

@dantman
Copy link

dantman commented Jul 5, 2022

so provided arguments should be stable, it doesn't make any non-stable variables stable.

This RFC is about making events with non-stable deps stable, not stuff that you can just do with useCallback.

If some non-state-variable is needed as stable one, it might be used with existing mechanism that react provides to keep variables between re-renders -- useRef. Their updates won't cause any re-renders, but again we have to use the state updater function to update state variables.

Both updater functions and refs are stable and could be safely used as memoized bound (partially applied) arguments, which won't cause memoization break (updated demo). Is there any specific use-case where this wouldn't work?

So the whole useBoundHandler was an unrelated tangent. You're really arguing for using refs to make useCallback deps stable instead of a hook to make the callback stable.

The RFC's Alternates section has an answer to this:

Add an API to read the "latest" versions of arbitrary values instead. We find that this gets noisy in practice since a block of code often needs to read multiple values. Marking entire blocks of code (functions) instead of individual values is more convenient as the amount of code grows, and solves the same problem in a more generic way.

I should add your updated demo misses something important and has an issue.

The majority of the issues around non-stable useCallbacks dependencies is getting non-stable state into the function not out of it. The demo doesn't cover this, it only covers cases where state is changed in the function. Properly managing a ref is the hard part, which the demo seems to have avoided instead using myStableVar.current in render in ways that should not be done.

@Viktor286
Copy link

Hey @dantman, i am not sure why you keep referring to useCallback, i've never suggested it, it would work differently in context of my example (specifically, it would recreate binds on each call of useCallback, making optimization useless).

About RFC, the goal stated there is to make following results:

... lets you define event handlers that can read the latest props/state but have always stable function identity. Event handlers defined ... don't break memoization and don't re-trigger effects.

That useBoundHandler thing does exactly that, except reading the latest props. Which hypothetically could be solved by passing ref object into applied arguments, since ref shares the value between re-renders but keeps stable identity.

You probably got me wrong, I am not arguing nor saying that this is how it should be done. All i've noticed that there is the way to basically do the same thing suggested in RFC. Yes with code overhead. But with current tools & without extra magic and with one additional benefit -- ability to extract callback out of component. If you think that won't work for some reason, please share the use-case example so we could try to work it through and maybe have a chance to make something good out of it.

@Viktor286
Copy link

For those who might be interested, continuing discussion with @dantman above, here is the demo example of usage and POC for custom useEvent hook implementation built with existed react tools only, without any new react functionality. In general it looks like it fits most of the functional parts of useEvent RFC description (but this particular experiment made out of pure technical interest and I don't promote it as a good pattern for real world practice).

The implementation based on partially applied arguments on memoized callback.
As addition to useEvent RFC it allows to extract event handlers out of the component.
For the details check out the file '../CustomHooks/useEvent'

image

@dantman
Copy link

dantman commented Jul 8, 2022

This useBoundHandler stuff seems really off-topic for this RFC. But I should bring up that the POC shared will not actually provide a stable identity for the callbacks.

Extracting the problem code:

const handleClick = useEvent({
  fn: handlers.handleClick,
  props: { setUID, setNewItemText, setItems, uid, newItemText, items },
});

// useEvent
const handleProps = useRef({}); 
handleProps.current = props;
useBoundHandler(fn, [handleProps.current]);

// useBoundHandler
useMemo(() => fn.bind(null, ...argArr), [...argArr]);

useBoundHandler(fn, [handleProps.current]); means handleProps.current is being passed to useMemo's deps array. Which is an normally an improper use of hooks. Though the handleProps.current = props; the line before means it's equivalent to useBoundHandler(fn, [props]);.

However because props is an object this means that the useMemo in useBoundHandler will invalidate on every render making it behave as if there was no memo.

You could fix this by putting them into one function, using handleProps for the deps, use handleProps.current in your useMemo, and replace the fn.bind with an anonymous so you get a live value from handleProps.current instead of letting fn.bind bind an old value and never update it.

However even if you did all that you're modifying handleProps.current in render which should not be done. You could workaround this with useLayoutHandler. However that still doesn't update it at the correct point in time. Part of the useEvent RFC's purpose is that useEvent can know the exact point in the render lifecycle that the function w/closure should be updated.

Which loops us back to. If we had the RFC's useEvent, then whatever you want can be implemented in userland. But that stuff is off topic for the RFC.

@Viktor286
Copy link

This useBoundHandler stuff seems really off-topic for this RFC. But I should bring up that the POC shared will not actually provide a stable identity for the callbacks.
...
Which loops us back to. If we had the RFC's useEvent, then whatever you want can be implemented in userland. But that stuff is off topic for the RFC.

Hey @dantman, thanks for looking into that and for all the details! And thanks for noticing the missing part there, that was quite significant detail. From a critical pov i would also add that based on useMemo docs we shouldn't rely on consistent work of useMemo in general. Anyway, it was interesting thing to try. Cheers.

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

@dante01yoon
Copy link

where can I find real implementation of useEvent?

@BasixKOR
Copy link

@dante01yoon For real implementation inside React: facebook/react#24505 (WIP) but you can use the code inside the RFC to achieve similar behaviour.

@nmay231
Copy link

nmay231 commented Aug 10, 2022

Another reason to choose a different name than useEvent is that it looks too much like useEffect at first glance. While I would prefer useHandler or useEventHandler, useStable or useStableFn or useStableScope or useCurrentScope might work instead. Other than that, this is perfect. 👍

@dantman
Copy link

dantman commented Aug 10, 2022

Another reason to choose a different name than useEvent is that it looks too much like useEffect at first glance.

Usage is completely different, do these look alike?

useEffect(() => {
  // ...
}, [foo]);

const click = useEvent(() => {
  // ...
});

@nmay231
Copy link

nmay231 commented Aug 10, 2022

@dantman Obviously, in code they look different, but in documentation and casual discussion they can be confused.

EDIT: And what if a newcomer to React had tab completed the wrong function and was wondering why the code didn't work? While distinguishing names is not the end goal, I do think it's important.

@poteto poteto mentioned this pull request Sep 9, 2022
2 tasks
poteto added a commit to facebook/react that referenced this pull request Sep 14, 2022
This commit adds a new hook `useEvent` per the RFC [here](reactjs/rfcs#220), gated as experimental. 

Co-authored-by: Rick Hanlon <rickhanlonii@gmail.com>
Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>
@gaearon
Copy link
Member Author

gaearon commented Sep 27, 2022

Hey folks, thanks to everyone for the discussion! We've implemented a prototype in facebook/react#25229 so you can play with it in the @experimental releases. However, we've also realized that we don't feel confident in coupling the two different motivations (rendering optimization and "fixing" Effects) under this single proposal.

In particular, we're worried that this proposal creates a strong incentive to wrap every function that currently uses useCallback into useEvent. However, that wouldn't lead to right semantics where the function identity is important. Since useEvent "erases" reactivity of the values flowing into it, we think there need to be additional limitations on where it makes sense to do the wrapping. In most cases, only the final consuming side (e.g. the Effect from which the event function is called) can be "sure" whether it "cares" about the reactivity of the function value. So using it widely seems premature.

For rendering optimizations, we are researching and developing an auto-memoizing compiler. It's not yet clear if the optimization described in this RFC would still be impactful when the compiler is on, so we will be revisiting this question as a part of that ongoing research. There are also other alternative ways to optimize this pattern that we might want to consider.

For now, we would like to shelve the current RFC, with a plan to publish a different, more scoped down RFC that supersedes this one. Since the new RFC will be different in scope, we might also give that Hook a different name.


tl;dr

  • We're still committed to solving both problems in Motivation, but maybe not at the same time.
  • We'll submit a more focused RFC to deal with the second motivation (preventing re-triggering of useEffect).
  • We expect that updated RFC will be similar to this one but have slight differences in usage and possibly naming.

@gaearon gaearon closed this Sep 27, 2022
@landisdesign
Copy link

Thank you, @gaearon and Team, for putting this out and moderating the sometimes contentious debates. I appreciate the goal behind this RFC and look forward to its replacement.

@gaearon
Copy link
Member Author

gaearon commented Sep 28, 2022

To clarify, the current plan is for the next RFC to have the same API, but with slightly different usage recommendations, slightly different semantics, and possibly a different name. I’ve seen some posts going around saying this RFC is “dead”, and that seems to give an impression that we’re abandoning this entire approach. In reality, we want to split this proposal and add a few extra constraints to it.

@mrcljx
Copy link

mrcljx commented Sep 29, 2022

In particular, we're worried that this proposal creates a strong incentive to wrap every function that currently uses useCallback into useEvent. However, that wouldn't lead to right semantics where the function identity is important.

In case this datapoint is helpful for the other RFCs, we implemented a slightly modified version of the proposed useEffect to help with the semantics.

Implementation:

  1. The implementation is the user-space implementation.
    1. Future: We'll probably switch to the experimental implementation.
  2. Our signature requires that the callback's return type must be (async) void.
    function useEvent<T extends (...args: any[]) => void | Promise<void>>(callback: T): T;
    
    1. A useEventWithResult variant exists that allows non-void results. It was deliberately picked to be "longer" to write and comes with a big "be sure that this isn't a case for useCallback" warning.
    2. Alternatives considered: We originally planned to write an ESLint rule to ensure that the variables that useEvent results are stored in are named {on,handle,set}XYZ, however solving this through seemed to better fit the semantics.
    3. Future: we might be a bit less strict about Promise and actually allow void | Promise<unknown>.
  3. We forked eslint-plugin-react-hooks to treat the return values of these new hooks as stable when it comes to exhaustive-deps.
  4. We then went through our codebase and replaced each const {on,handle,set}XYZ = useCallback with useEvent. A few cases were pointed out where the callback actually returned a value and needed to be replaced useEventWithResult.

Learnings:

  1. Our codebase now roughly has a 1:4 share of useCallback to useEvent (120 vs 430).
    1. It means in those 430 cases we were able to remove the deps array.
    2. Because of the void-constraint we are very sure that this was correct semantically.
  2. The term useEvent is still a bit odd.
    1. Intent is a bit unclear.
    2. Conflict with the concept of "events" (we had to renamed a different hook to useDOMEvent).
    3. Future: If React would not implement this RFC, we'd consider switching to useHandler (or useEventHandler) and useHandlerWithResult as a nod to the class-based component convention of naming event handlers (fun fact: in C# an EventHandler is required to be void as well).

@jampy
Copy link

jampy commented Oct 3, 2022

Our signature requires that the callback's return type must be (async) void.

IMHO the question useCallback vs useEvent is not about the return type.

As @gaearon says:

In most cases, only the final consuming side (e.g. the Effect from which the event function is called) can be "sure" whether it "cares" about the reactivity of the function value.

I second that. It's not about the implementation. It's about the consumer.

rickhanlonii added a commit to facebook/react that referenced this pull request Oct 5, 2022
This commit adds a new hook `useEvent` per the RFC [here](reactjs/rfcs#220), gated as experimental. 

Co-authored-by: Rick Hanlon <rickhanlonii@gmail.com>
Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>
@yolio2003
Copy link

what about useNonReactiveHandler

@sgarcia-dev
Copy link

sgarcia-dev commented Oct 21, 2022

@mrcljx your findings are really interesting. It's great to see that even if the RFC isn't finalized, the core idea behind this is beyond a POC and viable with the correct usage.

3. We forked eslint-plugin-react-hooks to treat the return values of these new hooks as stable when it comes to exhaustive-deps.

Do you mind sharing how you achieved this? We're curious to try this out but couldn't quite figure out the change necessary for this to work. The isStableKnownHookValue appears to be the function in charge of marking dependencies as safe to ignore.
https://github.com/facebook/react/blob/c635807875630e7057777e898372eed43e3b0a24/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L164

Did you just add an additional scenario around the following line with code similar to this and re-build the extension?
https://github.com/facebook/react/blob/c635807875630e7057777e898372eed43e3b0a24/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L226-L228

if (name === 'useEvent' && id.type === 'Identifier') { // "useEvent" being the name of the custom hook
  return true
}

Thanks!

@mrcljx
Copy link

mrcljx commented Oct 21, 2022

@sgarcia-dev Yes, this is our patch (we also patched in useLatest which returns a Ref like useRef).

         }
         const id = def.node.id;
         const { name } = callee;
-        if (name === "useRef" && id.type === "Identifier") {
+        if (name === "useLatest" && id.type === "Identifier") {
+          // useLatest() return value is stable.
+          return true;
+        } else if (name === "useEvent" && id.type === "Identifier") {
+          // useEvent() return value is stable.
+          return true;
+        } else if (name === "useRef" && id.type === "Identifier") {
           // useRef() return value is stable.
           return true;
         } else if (name === "useState" || name === "useReducer") {

pawelgrimm added a commit to Doist/typist that referenced this pull request Jan 13, 2023
The `useEvent` function was being called during rendering,
which throws an error when React's Strict Mode is enabled. This
type of usage is incorrect because it makes the rendering result
non-determinisitic.

See reactjs/rfcs#220 (comment)
davismcphee added a commit to elastic/kibana that referenced this pull request Jan 18, 2023
…146352)

## Summary

This PR includes a number of updates to Unified Histogram to prepare for
sharing with solutions, as well as updating its usage in Discover:

Unified Histogram:
- Adds a `disableAutoFetching` prop, similar to Unified Field List, to
disable automatic Unified Histogram refetching based on prop changes.
- Accepts an `input$` observable prop that can be used to control manual
refetches.
- Removes `refetchId` used internally and replaces it with an
observables based approach to simplify refetch logic.
- Introduces a `use_stable_callback` utility hook to create callback
functions with stable identities which simplifies `useCallback` logic —
should be replaced with `useEvent` or whatever the React team comes up
with to solve this specific issue when available:
reactjs/rfcs#220.
- Eliminates debouncing logic in Unified Histogram since it was hacky —
manual refetching should be used instead if debouncing is needed.
- Accepts `query`, `filters`, and `timeRange` props to remove
dependencies on specific services.
- Updates `use_request_params` to export `getTimeRange` and
`updateTimeRange` functions for easier absolute time range handling.
- Exposes some Lens embeddable props to allow further customizing
Unified Histogram behaviour.

Discover:
- Exports a `fetch$` observable from `use_saved_search` to allow
listening for when data fetches should be triggered.
- Uses new manual refetching support in Unified Histogram to simplify
refetching logic.
- Passes `query`, `filters`, and `timeRange` props to Unified Histogram.

### Checklist

- [ ] ~Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)~
- [ ]
~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials~
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] ~Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard
accessibility](https://webaim.org/techniques/keyboard/))~
- [ ] ~Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))~
- [ ] ~If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~
- [ ] ~This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))~
- [ ] ~This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)~

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
wayneseymour pushed a commit to wayneseymour/kibana that referenced this pull request Jan 19, 2023
…lastic#146352)

## Summary

This PR includes a number of updates to Unified Histogram to prepare for
sharing with solutions, as well as updating its usage in Discover:

Unified Histogram:
- Adds a `disableAutoFetching` prop, similar to Unified Field List, to
disable automatic Unified Histogram refetching based on prop changes.
- Accepts an `input$` observable prop that can be used to control manual
refetches.
- Removes `refetchId` used internally and replaces it with an
observables based approach to simplify refetch logic.
- Introduces a `use_stable_callback` utility hook to create callback
functions with stable identities which simplifies `useCallback` logic —
should be replaced with `useEvent` or whatever the React team comes up
with to solve this specific issue when available:
reactjs/rfcs#220.
- Eliminates debouncing logic in Unified Histogram since it was hacky —
manual refetching should be used instead if debouncing is needed.
- Accepts `query`, `filters`, and `timeRange` props to remove
dependencies on specific services.
- Updates `use_request_params` to export `getTimeRange` and
`updateTimeRange` functions for easier absolute time range handling.
- Exposes some Lens embeddable props to allow further customizing
Unified Histogram behaviour.

Discover:
- Exports a `fetch$` observable from `use_saved_search` to allow
listening for when data fetches should be triggered.
- Uses new manual refetching support in Unified Histogram to simplify
refetching logic.
- Passes `query`, `filters`, and `timeRange` props to Unified Histogram.

### Checklist

- [ ] ~Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)~
- [ ]
~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials~
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] ~Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard
accessibility](https://webaim.org/techniques/keyboard/))~
- [ ] ~Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))~
- [ ] ~If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~
- [ ] ~This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))~
- [ ] ~This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)~

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment