Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

197
Views
RXJS observables cancel mouseover stream after mouseout event

Trying to implement an rxjs observable pipeline in React.

Codesandbox example -> https://codesandbox.io/s/optimistic-fog-xtdi4

Creating a html element (div) in React, and binding mouseover and mouseout events.

React Code to initialize this:

   useEffect(() => {
    const elMouseOut$ = fromEvent(elRef.current, "mouseout").subscribe(() =>
      setState({ hover: "mouseout", eventType: "mouseout" })
    );

    const elMouseOver$ = fromEvent(elRef.current, "mouseover")
      .pipe(debounceTime(2000))
      .subscribe(() =>
        setState({ hover: "mouseover", eventType: "mouseover" })
      );

    return () => {
      elMouseOver$.unsubscribe();
      elMouseOut$.unsubscribe();
    };
  }, []);

Acceptance criteria Steps.

  1. When I mouse over, after a delay of 2 seconds i need to make a state update.
  2. If I mouse out at any time BEFORE the 2 second delay I need to cancel the mouseover stream, and set some state. (THIS STEP IS BROKEN)
  3. If I mouse out any time AFTER the 2 second delay, I need to set some state.
  4. This should be repeatable, in the sense that if i mouse over, mouse out and mouse over again we start from Step 1.

I have tried several different combinations of operators, but cannot get things working as expected.

Note: If the mouse over debounce time is met, an ajax request is made.

Any help/advice would be appreciated, i simply do not have the rxjs chops to sort this one out.

about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

This should do the job:

useEffect(() => {
    const elMouseOver$ = fromEvent(elRef.current, "mouseover");
    const elMouseOut$  = fromEvent(elRef.current, "mouseout");

    const mouseoutSub = elMouseOver$.pipe(
        debounceTime(2000),
        tap(() => setState({ hover: 'mouseover', eventType: 'mouseover' })),
        switchMap(() => elMouseOut$),
        tap(() => setState({ hover: 'mouseout', eventType: 'mouseout' })),
        takeUntil(scheduled(elMouseOut$, asyncScheduler)),
        repeat()
    );

    return () => mouseoutSub.unsubscribe();
}, []);

Flow:

  • debounceTime - after mouseover, wait 2 seconds
  • tap - set mouseover state
  • switchMap - listen for elMouseOut$
  • tap - set mouseout state
  • takeUntil - end stream when mouseout occurs
  • repeat

Since switchMap and takeUntil are using the same source, I used scheduled inside the takeUntil so that the switchMap would have a chance to emit before the observable was completed.


Here's a StackBlitz that demonstrates this behavior.

about 4 years ago · Juan Pablo Isaza Report

0

CodeSandbox solution.


I think this could be a way to solve it:

const sub = merge(
  elMouseOver$.pipe(
    // Step 4)
    // Repeating the steps with the help of `switchMap`
    switchMap(() =>
      timer(2000).pipe(
        // Step 2).
        // Cancelling the stream and setting some state(have a look at '3)', below).
        takeUntil(elMouseOut$),

        // Step 1).
        // This will be invoked if 2 seconds have passed after the mouseover event without
        // the mouseout event taking place.
        tap(() => setState({ hover: "mouseover", eventType: "mouseover" })),
      )
    )
  ),
  // Step 3).
  // Setting the state.
  elMouseOut$.pipe(
    tap(() => setState({ hover: "mouseout", eventType: "mouseout" }))
  )
).subscribe();

Also, because we're subscribing to elMouseOut$ multiple times, you might want to use the share() operator so that only one event listener will be added:

const elMouseOut$ = fromEvent(elRef.current, "mouseout").pipe(
  share(),
);
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!