9

I'm working on a react project. I have my own API to fetch information. I'm using the useEffect hook to fetch profile information from API. My problem is when page mounts for the first time i can fetch the data with no problem but if i refresh the page it doesn't work at all. I know i have to give a second parameter to useEffect. I tried to put profile as the second argument even dispatched the getCurrentProfile function but when i do that it constantly fires off fetch request. I would be glad if anyone can help me with that. Thanks.

Here is my Profile component:

export const Profile = () => {
const dispatch = useDispatch();
useEffect(() => {
    dispatch(getCurrentProfile());

}, [])

const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;



const { user } = authReducer;

console.log("loading", loading)
console.log("profile", profile)

return loading && profile === null ? (
    <div >
        <Spinner />
    </div>
) :

Here is my Profile action:

export const getCurrentProfile = () => async dispatch => {

try {
    const res = await axios.get("/api/profile/me");
    console.log(res);
    dispatch({
        type: "GET_PROFILE",
        payload: res.data.data
    })
} catch (err) {
    dispatch({
        type: "PROFILE_ERROR",
        payload: { msg: err.response.statusText, status: err.response.status }
    })
}

}

Here is my profile reducer:

export default (state = initialState, action) => {
const { type, payload } = action;

switch (type) {
    case "GET_PROFILE":
        return {
            ...state,
            profile: payload,
            loading: false
        }

    case "PROFILE_ERROR":
        return {
            ...state,
            error: payload,
            profile: null
        }
    case "CLEAR_PROFILE":
        return {
            ...state,
            profile: null,
            loading: false
        }
    default:
        return state;
}

}

hakanAkdogan
  • 223
  • 2
  • 3
  • 10
  • 1
    could you add in to the question what it was that you put into the dependency array (the second argument to useEffect) that caused it to continually fetch? – Dakota Lee Martinez Apr 16 '21 at 23:24

2 Answers2

3

You might want to try adding conditional logic within the useEffect so you only trigger the dispatch if you don't already have a profile.

import "./styles.css";
import { useDispatch, useSelector } from "react-redux";
import { useEffect, useCallback } from "react";
import { getCurrentProfile } from "./action";

export const Profile = () => {
  const dispatch = useDispatch();
  const profileReducer = useSelector((state) => state.profile);
  const authReducer = useSelector((state) => state.auth);
  const { profile, error, loading } = profileReducer;
  // read more about this here: https://stackoverflow.com/questions/58624200/react-hook-useeffect-has-a-missing-dependency-dispatch
  const stableDispatch = useCallback(dispatch, []);

  useEffect(() => {
    if (!profile) {
      stableDispatch(getCurrentProfile());
    }
  }, [profile, stableDispatch]);

  const { user } = authReducer;

  console.log("loading", loading);
  console.log("profile", profile);

  return loading && profile === null ? <div>Spinner</div> : "Actual Profile";
};

export default Profile;

Also, it doesn't seem like you're currently doing anything with the loading piece of state–at least from what you've shared here. You might want to dispatch an action indicating that you're loading before you start the fetch and then it will be set to false when you get the response.

Check out this codesandbox for reference: https://codesandbox.io/s/focused-kilby-gd2nr?file=/src/App.js

Reducers:

const initialState = {
  profile: null,
  loading: false
};
export const profile = (state = initialState, action) => {
  const { type, payload } = action;

  switch (type) {
    case "LOADING_PROFILE":
      return {
        ...state,
        loading: true
      };
    case "GET_PROFILE":
      return {
        ...state,
        profile: payload,
        loading: false
      };

    case "PROFILE_ERROR":
      return {
        ...state,
        error: payload,
        profile: null
      };
    case "CLEAR_PROFILE":
      return {
        ...state,
        profile: null,
        loading: false
      };
    default:
      return state;
  }
};

export const auth = (state = {}, action) => {
  return state;
};

Action Creator:

import axios from "axios";
export const getCurrentProfile = () => async (dispatch) => {
  try {
    dispatch({ type: "LOADING_PROFILE" });
    const res = await axios.get("https://jsonplaceholder.typicode.com/users/1");
    console.log(res);
    dispatch({
      type: "GET_PROFILE",
      payload: res.data.data
    });
  } catch (err) {
    dispatch({
      type: "PROFILE_ERROR",
      payload: { msg: err.response.statusText, status: err.response.status }
    });
  }
};

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { profile, auth } from "./reducers";
import App from "./App";
import thunk from "redux-thunk";

const store = createStore(
  combineReducers({
    profile,
    auth
  }),
  applyMiddleware(thunk)
);

const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>,
  rootElement
);

  • So i tried adding a conditional logic to useEffect. To do that i had to declare profileReducer before the useEffect. I put profile to dependecy array. It still works when first mounted but refreshing the page doesn't fires useEffect at all. – hakanAkdogan Apr 17 '21 at 00:09
  • 1
    hmm, interesting. Have you checked out the sandbox I shared? Seems like it's working on refresh there. I did also add a bit about loading to the action creator and reducer – Dakota Lee Martinez Apr 17 '21 at 00:10
  • Thanks for all your help. It turns out it was a silly mistake i made. I published the mistake i made you can check it out if you want. Sorry for stealing your time, thank you again :) – hakanAkdogan Apr 17 '21 at 00:19
  • 1
    no worries! thanks for sharing the fix. I think there can be an issue sometimes with adding a function into the dependency array and that it's generally encouraged to use the useCallback hook to create a stable version of the function so it's not a new function every time. https://reactjs.org/docs/hooks-reference.html#usecallback – Dakota Lee Martinez Apr 17 '21 at 00:23
  • I recently got a similar issue but not the same, the 2 condition branches both are reached under useEffect, https://stackoverflow.com/questions/76464195/inside-of-useeffect-both-condition-branches-got-triggered-when-refresh-page – tim Jun 13 '23 at 11:07
2

Well i solved it by dispatching 'getCurrentProfile' not 'getCurrentProfile()' turns out using it like a function causes continuously firing off.

   const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;
const dispatch = useDispatch();

useEffect(() => {
    if (!profile) {
        console.log("It worked")
        dispatch(getCurrentProfile());
    }




}, [dispatch(getCurrentProfile)])
hakanAkdogan
  • 223
  • 2
  • 3
  • 10