2

I have a React component using the react-polling library to poll an endpoint. It will keep on polling till the expected response is obtained. During polling, it shows a message that polling is in progress. After polling terminates, it will show a success response.

A quick overview of the key props of the Polling component:

  • onSuccess - callback that takes the axios response and returns true to continue polling and false to stop polling
  • onFailure - callback called when the API/endpoint call wasn't successful.
  • render - Renders content

The entire implementation is present in ReactPolling.js. Please take a quick look/skim if required (especially the render method).

Code organization

I have a main App which renders Page1Component. It uses the Polling component which uses the above ReactPolling library. I have a prop named renderOnSuccess of type () => JSX.Element | void. It is a function which I call after the polling is successful. It could return

  1. A JSX.Element which is the content to be shown after the polling is completed successfully. This will be set as a state variable and will be rendered.
  2. Return nothing - in this case, nothing will be shown after polling is successful.

Case 1 (Render success message content on same page/component): (Works fine)

I return a content from the renderOnSuccess method and it will be shown after polling is completed.

Codesandbox code for polling success case 1.

Output would be:

Page 1
Hello I am polling

..after polling completes..

Page 1
Success

Note that the Success message is shown from the same page/component (Page1Component)

Case 2 (Render new page after polling) (Problematic)

In this case, I want to change the top-level component itself. So, I change to page 2 from page 1.

For this, I maintain a state variable to track the page to be rendered and change it from the renderOnSuccess callback in App.js.

Codesandbox code for polling success case 2.

Output in this case is

Page 1
Hello I am polling

..after polling completes..

Page 2
Page 2 content

In this case, I get a warning:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

After reading posts on SO for the above warning (example), I have added a useRef to check if the component is still mounted or not before setting contentToBeRendered in the Polling component and now the warning goes away.

Codesandbox code for polling warning fix

Code for fix in Polling component:

const unmounted = useRef(false);

useEffect(() => {
  return () => {
    unmounted.current = true;
  };
}, []);

//In onSuccess
let content = props.renderOnSuccess() || null;
if (!unmounted.current) {
  setContentToBeRendered(content);
}

Question:

Is this the right way to handle the warning?

The fixes recommending to use useRef usually involve making an async call. Thus, the component could be unloaded before the call returns. But here, the call to renderOnSuccess would never return as the parent component rendered some other page (The setContentToBeRendered will never happen).

I'm doubtful if this is the right fix and is there still a memory leak because the call to renderOnSuccess never returns in case 2.

Thiyagu
  • 17,362
  • 5
  • 42
  • 79
  • have you tried using the Cancellation Axios feature? – R.M. Reza Oct 22 '21 at 09:35
  • @R.M.Reza The issue is not when making API calls. It is after we get a successful response for the polling to stop - we call the caller passed callback to render something on the page. But in case 2, the caller decided to change the top-level page/component which led to the React warning. – Thiyagu Oct 22 '21 at 09:52

0 Answers0