Why doesn't useEffect hook work on page refresh?

11,109

Solution 1

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
);

Solution 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)])
Share:
11,109
hakanAkdogan
Author by

hakanAkdogan

Updated on June 05, 2022

Comments

  • hakanAkdogan
    hakanAkdogan almost 2 years

    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;
    }
    

    }

    • Dakota Lee Martinez
      Dakota Lee Martinez about 3 years
      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?
  • hakanAkdogan
    hakanAkdogan about 3 years
    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.
  • Dakota Lee Martinez
    Dakota Lee Martinez about 3 years
    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
  • hakanAkdogan
    hakanAkdogan about 3 years
    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 :)
  • Dakota Lee Martinez
    Dakota Lee Martinez about 3 years
    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. reactjs.org/docs/hooks-reference.html#usecallback