Back

Technologies:

javascriptjavascript
reactjsreactjs
avatar
Tolerim
24 days ago

Can debounceTime in RxJS React be configured to trigger only once?

Many people ask how to make a function trigger once, but I'm facing the opposite problem: my function is only triggering once when it should be triggering multiple times. I have created a CodeSandbox that demonstrates the issue.

The code is for a basic todo list app, specifically the list aspect. Each item has a handletextchange() function that updates the item's name and completion status. I want to use RxJS debounce to collect together changes and make them all at once. The handlesubmitchanges() function currently just logs that it has been called and updates the knowntodolist, which stores the difference for the next submit and serves as a fallback if the submit fails.

Here is the relevant code:

export const TodoList: React.FC<TodoListProps> = ({ todos }) => {
  const [todolist, settodo_list] = useState<Todo[]>(todos);
  const [knowntodolist, setknowntodolist] = useState<Todo[]>(todos);
  const todolistsubject = new Subject<Todo[]>();

  const handletextchange = (id: number, text: string, completed: boolean) => {
    console.log("handletextchange", { id, text, completed });
    settodolist((prevtodolist) => {
      const updatedtodolist = prevtodolist.map((todo) =>
        todo.id === id ? { ...todo, text, completed } : todo
      );
      console.log(`next`);
      todolistsubject.next(updatedtodolist);
      return updatedtodolist;
    });
  };

  const handlesubmitchanges = () => {
    console.log("handlesubmitchanges", todo_list);
    setknowntodolist(todolist);
  };

  const handletodolist_observer: Observer<Todo[]> = {
    next: handlesubmitchanges,
    error: (err: any) => console.error(err),
    complete: () => console.log("todolistobserver complete")
  };

  useEffect(() => {
    console.log("subscription");
    const subscription = todolistsubject
      .pipe(debounceTime(1000))
      .subscribe(handletodolist_observer);
    return () => {
      console.log("unsubscribe");
      subscription.unsubscribe();
    };
  }, []); //Removing this dependency array makes it subscribed and unsubscribe on every change making it not work

  return (
    <ul>
      {todo_list.map((todo) => (
        <TodoItem key={todo.id} todo={todo} onTodoChange={handletextchange} />
      ))}
    </ul>
  );
};
After clicking the middle checkbox a few times, I see the following output. The teal lines indicate that enough time has passed that a debounce should have occurred, but it didn't. I'm not sure why my function is only running once, or why reactdevtoolsbackend.js jumps in to log "next" too but only on the second time it fails onwards.

screenshot of console log
Answers(2)
avatar
Vogi
23 days ago
Verified Answer
It seems like you are having trouble with RxJS debounce not working. I found a similar issue on StackOverflow where the user was having trouble with debounceTime not working. The solution was to use debounce instead of debounceTime. I hope this helps! Let me know if you have any other questions.

Sources

Debounce is not working · Issue #1212 · Reactive-Extensions/RxJS - GithubAngular Jest tick not working for debounceTime on RxJS 7Angular Jest tick not working for debounceTime on RxJS 7RxJS debounce() Filtering Operator - javatpointjavascript - Rx DebounceTime does not work - Stack OverflowRxJs 6 DebounceTime operator not working properly
avatar
Tolerim
24 days ago
Based on the code you have shared, it seems that you are using RxJS to debounce changes in a todo list before submitting them. However, it looks like the debounce time is not working as expected and the submit function is only triggering once instead of multiple times. One possible reason for this issue could be that you are not providing a dependency array in your useEffect() hook. By not including a dependency array, the effect is triggered on every render which may be causing the debounce time to be reset each time. To fix this issue, you can add the todo_list state as a dependency in the dependency array of your useEffect() hook. This will ensure that the effect only executes when the todo_list state changes, which will allow the debounce time to work as expected and trigger the submit function multiple times. Here's what the updated code for your useEffect() hook would look like:
useEffect(() => {
    console.log("subscription");
    const subscription = todo_list_subject
      .pipe(debounceTime(1000))
      .subscribe(handle_todo_list_observer);
    return () => {
      console.log("unsubscribe");
      subscription.unsubscribe();
    };
  }, [todo_list]); // add todo_list as dependency
By adding todo_list as a dependency, the useEffect() hook will only execute when the todo_list state changes, which should allow the debounce time to work correctly and trigger the submit function multiple times. I hope this helps! Let me know if you have any further questions.
;