Search code examples
reactjsreact-reduxuse-effectredux-toolkit

useEffect is on a infinity loop even with an empty dependency array


I dispatching a function using my useEffect to populate my state when the page is mounted. The useEffect is on a infinity loop, even if i put an empty dependency array.

const token = JSON.parse(localStorage.getItem("xibo"));
  const access_token = token.access_token;

  useEffect(() => {
    
      dispatch(getDisplays(access_token));
      
  }, []);

I already try to put some dependencies but with the same result.

const token = JSON.parse(localStorage.getItem("xibo"));
  const access_token = token.access_token;

  useEffect(() => {
    
      dispatch(getDisplays(access_token));
    
  }, [access_token, dispatch]);

Any idea how to fix this problem?

this is the full code of the file

import React, { useEffect } from "react";
import { WaveLoading } from "react-loadingg";
import { useDispatch, useSelector } from "react-redux";
import { getDisplays } from "../features/xiboAuth/xiboSlice";

const DisplayList = () => {
  const dispatch = useDispatch();
  const { isLoading } = useSelector((store) => store.xibo);

  const token = JSON.parse(localStorage.getItem("xibo"));
  const access_token = token.access_token;

  useEffect(() => {
    dispatch(getDisplays(access_token));
  }, [access_token, dispatch]);

  return (
    <>
      {isLoading ? (
        <WaveLoading />
      ) : (
        <div className="container-fluid">
          <div className="title_table">
            <h3>List of displays</h3>
          </div>
          <table className="table table-striped">
            <thead>
              <tr>
                <th className="col">#</th>
                <th className="col">name</th>
                <th className="col">type</th>
                <th className="col">ip</th>
              </tr>
            </thead>
            <tbody>
              <tr></tr>
            </tbody>
          </table>
        </div>
      )}
    </>
  );
};

export default DisplayList;

this is the file that call this component

import React, { useEffect } from "react";
import { WaveLoading } from "react-loadingg";
import { useDispatch, useSelector } from "react-redux";
import { toast } from "react-toastify";
import DisplayList from "../components/DisplayList";
import { getDisplays, getToken } from "../features/xiboAuth/xiboSlice";

const Dashboard = () => {
  const { user } = useSelector((store) => store.auth);
  const { isError, message, isLoading, xibo } = useSelector(
    (store) => store.xibo,
  );

  const dispatch = useDispatch();

  useEffect(() => {
    if (isError) {
      toast.error(message);
    }
    if (user) {
      dispatch(getToken());
    }
  }, [user, isError, message, dispatch]);


  return (
    <>
      {isLoading ? (
        <WaveLoading />
      ) : (
        <div className="col py-3">
          <div className="d-flex dash_dsp_list">
            <DisplayList />
          </div>
        </div>
      )}
    </>
  );
};

export default Dashboard;

and this is the reducer that the useEffect must call only once.

  import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
  import xiboService from "./xiboService.js";

  const data = JSON.parse(localStorage.getItem("xibo"));

  const initialState = {
    xibo: data ? data : null,
    displays: null,
    isError: false,
    isLoading: false,
    isSuccess: false,
    message: "",
  };

  export const getToken = createAsyncThunk("xibo/getToken", async (thunkAPI) => {
    try {
      const response = await xiboService.getAccessToken();

      if (response.data.status === 201) {
        localStorage.setItem("xibo", JSON.stringify(response.data.data));
      }

      return response;
    } catch (error) {
      const message =
        (error.response && error.response.data && error.response.data.message) ||
        error.message ||
        error.toString();

      return thunkAPI.rejectWithValue(message);
    }
  });

  export const getDisplays = createAsyncThunk(
    "xibo/getDisplays",
    async (data, thunkAPI) => {
      try {
        return await xiboService.getDisplayList(data);
      } catch (error) {
        const message =
          (error.response &&
            error.response.data &&
            error.response.data.message) ||
          error.message ||
          error.toString();

        return thunkAPI.rejectWithValue(message);
      }
    },
  );

  export const xiboSlice = createSlice({
    name: "xibo",
    initialState,
    reducers: {
      reset: (state) => {
        state.isError = false;
        state.isSuccess = false;
        state.isLoading = false;
        state.message = "";
      },
    },
    extraReducers: (builder) => {
      builder
        .addCase(getToken.pending, (state) => {
          state.isLoading = true;
        })
        .addCase(getToken.fulfilled, (state, action) => {
          state.isLoading = false;
          state.isSuccess = true;
          state.xibo = action.payload;
        })
        .addCase(getToken.rejected, (state, action) => {
          state.isLoading = false;
          state.isError = true;
          state.message = action.payload;
          state.xibo = null;
        })
        .addCase(getDisplays.pending, (state) => {
          state.isLoading = true;
        })
        .addCase(getDisplays.fulfilled, (state, action) => {
          state.isLoading = false;
          state.isSuccess = true;
          state.displays = action.payload;
        })
        .addCase(getDisplays.rejected, (state, action) => {
          state.isLoading = false;
          state.isError = true;
          state.message = action.payload;
          state.displays = null;
        });
    },
  });

  export const { reset } = xiboSlice.actions;
  export default xiboSlice.reducer;

Solution

  • In your first example you said that your useEffect fires repeatedly even with an empty dependency array. What this means is that your DisplayList component is being recreated continuously. Obviously the issue is in your Dashboard component and it would have to be something in the useEffect.