Home > Software engineering >  How do I use spread operator in useEffect without infinite loop?
How do I use spread operator in useEffect without infinite loop?

Time:05-12

I code a Chrome extension. popup.html is implemented in React as follows:

const [blogUrls, setblogUrls] = useState<string[]>([]);

useEffect(() => {
  chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    const newBlogUrls = [...blogUrls, ...request.param.blogUrls];
    setblogUrls(newBlogUrls);

    return true;
  });
}, [blogUrls]);

How do I avoid an infinite loop?

I know it's not good to write [blogUrls] even though I wrote setblogUrls(newBlogUrls).

But I want to write like [...blogUrls,.

Please tell me how to resolve this contradiction.

CodePudding user response:

Try this -

useEffect(() => {
  chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    const newBlogUrls = [...blogUrls, ...request.param.blogUrls];
    setblogUrls(newBlogUrls);

    return true;
  });

  return () => chrome.runtime.onMessage.removeEventListener();
}, []);

it will execute once after page render. your dependency blogUrls is changing after rendering page inside the useEffect itself. so useEffect calling itself again and again. So, its creating infinite loop. To render one time, just make sure it should be empty dependency in useEffect.

CodePudding user response:

  1. Your code adds a listener on every render.
// runs on every render
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  1. Remove the listener as -
const [blogUrls, setblogUrls] = useState<string[]>([]);

function onBlogUrlsChanged(request) {
    const newBlogUrls = [...blogUrls, ...request.param.blogUrls];
    setblogUrls(newBlogUrls);
}

useEffect(() => {
    chrome.runtime.onConnect.addListener(onBlogUrlsChanged);
    return () => {
      chrome.runtime.onConnect.removeListener(onBlogUrlsChanged);
    }
  }, []);

It's ok to run the useEffect only once by passing [] parameter., We don't wastefully attach and detach the listener every time the component is re-rendered.

CodePudding user response:

You can do something like this

useEffect(() => {
  chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    setblogUrls((v) => [...v, ...request.param.blogUrls]);

    return true;
  });

  // Don't forget to clean the listener
  return () => chrome.runtime.onMessage.removeEventListener()
}, []);
  • Related