2

I have a Provider component which passes down some data through its props, which will later be used across the app.

It looks something like this:

export const MyContext = createContext({});
MyContext.displayName = 'My Context';


export const MyProvider = ({ children }) => {
    const [items, setItems] = useState([]);
    const [loading, setLoading] = useState(false);

    const setData = async () => {
        setLoading(true);
        setItems(await getItemsApiCall());
        setLoading(false);
    };

    useEffect(() => {
        console.warn('component re-rendering')
        if (!items.length) {
            setData();
        }
    }, [items]);

    return (
        <MyContext.Provider value={{items, loading}}>
            {children}
        </MyContext.Provider>
    );
};

In theory this should work fine, however I am seeing a bunch of extra re-renders through the console.warn which seem unnecessary, also I am getting the warning relating to the state update on an unmounted component

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...

Although I have seen the solution for this in other threads, none of them work for me because I am not only setting items, but also loading, which I need to put in spinners and other such things on other parts of the app while fetching data.

Anyway, is there a way to prevent these re-render before the mount and to surpress the warning under these circumstances?

Edit: For the record, I am currently getting 2 re-renders before the warning relating to updating the state of an unmounted component, and then two other re-renders (those of which are to be expected). So basically, my console look something like this:

component re-rendering

component re-rendering

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...

component re-rendering

component re-rendering

Edit2: For what it is worth, I would also like to show how the MyProvider is being used. It is simply wrapping another component in a particular route. Something like this:

const App = () => (
    <Router>
        <Switch>
            <Route
            path='/items'
            exact
            component={() => (
                <MyProvider>
                    <Items/>
                </MyProvider>
            )}
            />
        </Switch>
    </Router>
)

The behaviour I get above happens simply when visiting the /items route.

theJuls
  • 6,788
  • 14
  • 73
  • 160
  • 1
    Typo `value={items, loading}` -> `value={{items, loading}}`? – Nemanja Lazarevic Dec 16 '20 at 21:29
  • I highly doubt its running before it's rendered. How many times is the console log running? From looking at your code it should be called twice. Once on load and once after `setItems` is called – azium Dec 16 '20 at 21:31
  • @NemanjaLazarevic indeed that was a typo, thanks. Keep in mind though, this is only example for the purpose of this post, so it is not anything affecting my actual code – theJuls Dec 16 '20 at 21:35
  • @azium the console.warn does run twice before getting the warning about the state being updated before the mount. And then twice after the warning. I am trying to get rid of the warning and the two times it runs before that warning. – theJuls Dec 16 '20 at 21:37
  • does the same thing happn if you move your `setData` function to be inside your `useEffect` block? it should definitely be inside anyways since async alls are effects, and should be inside `useEffect`s – azium Dec 16 '20 at 21:50
  • @azium Yup, the same thing happens if `setData` is inside the `useEffect` block. I also tried moving it into the `if (items.length)` block, but didn't make a difference either. I am still getting the console.warns and the same warning. – theJuls Dec 16 '20 at 22:05
  • It's impossible for the console warning to occur *before* the component is mounted as by definition the first effect is triggered *after* the component mounts and renders ***once***. As pointed out, ideally there would be only 2 warnings, the initial mounting render, and a second after state updates. You should check what is rerendering your `Route` and or `App` component that is causing your `MyProvider` to remount repeatedly. – Drew Reese Dec 16 '20 at 22:54
  • 1
    @DrewReese so basically what is causing the warning is perhaps a mount, followed by a dismount and an update in that in between? – theJuls Dec 16 '20 at 23:13
  • 2
    Seems likely. You can test by adding a `useEffect(() => console.log('mounted'), []);` and count how many you see. – Drew Reese Dec 16 '20 at 23:34

1 Answers1

0

Removes "items" from the list of dependencies of the effect as the setItems is being used in the setData and receives the callback.