gkelly
gkelly

Reputation: 288

Some insight on React behavior would be appreciated. Issue with custom hook

I have a small react example that is puzzling me.

You can run the code at codesanbox

The code is very simple, there is a component TestComponent that displays 'wowzers', and calls a custom hook useCustomHook.

useCustomHook does three things. It declares a useState [list, set_list], then a useEffect based on list to inform when there is a change, and then a 1 second timer that will update the list.

What I expected was that the useEffect would initially run and then a second later it would run again when the list is updated. However, it updates every second. Not only this, but the useCustomHook is being re-entered from TestComponent starting the process all over again, and this happens repeatedly.

Can someone explain please why, when the list is updated with set_list in the timer callback that this causes TestComponent to call useCustomHook again (and again)

I thought I understood that principles of using react and have developed numerous but small applications. This simple example is really throwing me off. Any help would be appreciated.

The code in index.js is a follows.

import { useState, useEffect } from "react";
import React from "react";
import ReactDOM from "react-dom";

const useCustomHook = () => {
  const [list, set_list] = useState([]);
  useEffect(() => console.log("useEffect", "list updated"), [list]);
  setTimeout(() => set_list(() => []), 1000);
};

const TestComponent = () => {
  const hook = useCustomHook();
  return <span>wowzers</span>;
};

class App extends React.Component {
  render() {
    return <TestComponent />;
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

Upvotes: 2

Views: 55

Answers (2)

Josh Lin
Josh Lin

Reputation: 2437

might skiped some step and not exactly how it gos, surely it will keep updating and the sequenced steps can help you some

import { useState, useEffect } from "react";
import React from "react";
import ReactDOM from "react-dom";

// called every render, because function component is called every render
const useCustomHook = () => {
  
  // 4 first useState, list=[] by default, alloc memory X
  // 9,15...  useState, list=[] changed by set_list, loaded from memory X 
  const [list, set_list] = useState([]);
  // 5 register effect, callback called every time list change
  // 10,16... effect alread registered, skip
  useEffect(() => 
    // 12,18... list updated because []!==[]
    console.log("useEffect", "list updated"), [list]);
  // 6,11,17... settimeout, callback in 1 sec
  setTimeout(() => 
    // 7,13,19... update memory X, triggering rerender
    set_list(() => []), 1000);
};

// function component is called every render
const TestComponent = () => {
  // 3,8,14...
  const hook = useCustomHook();
  return <span>wowzers</span>;
};

class App extends React.Component {
  render() {
    return <TestComponent />; //2
  }
}

ReactDOM.render(<App />, document.getElementById("root")); // 1

Upvotes: 1

Drew Reese
Drew Reese

Reputation: 202801

setTimeout is right in the hook body... each time the hook is called it runs the entire body. The timeout is enqueueing a state update which triggers a rerender. Rerenders run all the hooks again.

const useCustomHook = () => {
  const [list, set_list] = useState([]);

  useEffect(() => console.log("useEffect", "list updated"), [list]);

  setTimeout(() => set_list(() => []), 1000); // <-- unintentional side-effect!
};

What I expected was that the useEffect would initially run and then a second later it would run again when the list is updated.

Place the setTimeout in a mounting useEffect, i.e. empty dependency array, so it runs exactly once after the initial mounting render cycle.

const useCustomHook = () => {
  const [list, set_list] = useState([]);

  useEffect(() => console.log("useEffect", "list updated"), [list]);

  useEffect(() => {
    setTimeout(() => set_list(() => []), 1000);
  }, []);
};

Edit some-insight-on-react-behavior-would-be-appreciated-issue-with-custom-hook

Upvotes: 3

Related Questions