'How do I unsubscribe to the dispatch to grab new data onload on useEffect? - I'm using Redux Toolkit

I can load my data but only after I refresh the page. Until then, it shows the data from the previous item I clicked on. It's behaving like a cache would.

Here is my mediaSlice

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";

const KEY = process.env.REACT_APP_API_KEY
const BASE_URL = process.env.REACT_APP_BASE_URL
const HBO_SINGLE_MEDIA_API = `${BASE_URL}/titlestest`

const initialState = {
  media:{},
  status: 'idle',  //'idle', 'loading', 'succeeded', 'failed'  
  error:null
}

export const fetchSingleMediaTitle = createAsyncThunk(
  'media/fetchSingleMediaTitle', 
  async (id) => { 
    const response = await axios.get(
      HBO_SINGLE_MEDIA_API,
      {
        headers: {
          'Content-Type': 'application/json',
          'X-API-KEY': KEY,
        },
        params: {
          titleId: id,
        }
      }
    )

    return response.data.Item;
  } 
)

const mediaSlice = createSlice({
  name: 'media',
  initialState,
  reducers:{},
  extraReducers: {
    [fetchSingleMediaTitle.pending]: () => {
      console.log("Pending");
    },
    [fetchSingleMediaTitle.fulfilled]: (state, { payload }) => {
      state.status = 'succeeded'
      state.media = payload
    },
    [fetchSingleMediaTitle.rejected]: () => {
      console.log("Rejected");
    },
  }
})

// SELECTORS
export const selectSingleMedia = (state) => state.media.media;

export const getMediaStatus = (state) => state.media.status;

export default mediaSlice.reducer

And then the Media Component has what you would expect

const [media, setMedia] = useState([]);   
const {id} = useParams();
const dispatch = useDispatch();

const singlemedia = useSelector((state) => selectSingleMedia(state, id))
const mediaStatus = useSelector(getMediaStatus)

useEffect(() => { 
  if (mediaStatus === 'idle') {
    dispatch(fetchSingleMediaTitle(id)) //yes, it's imported
  }
    
  setMedia(singlemedia); 

  //it returns the array with the data but not the current one
  console.log("singlemedia: ", singlemedia); 

  return () => {  };
  // Someone suggested removing the dependencies below but then it doesn't load anything.
}, [id, singlemedia, mediaStatus, dispatch, media_data])

I am at a loss here because as I understand it, the useEffect is supposed to fire onload and give me the current data. The ID is correct in the params but the state is not mutating.

Thanks in advance

EDIT For reference, here is the console log. Those console logs are in the useEffect. The API is slightly slower than the useEffect and the render is happening before the data is ready (race condition) or at least that's what I think it's happening here that's why it loads empty and then it loads again. But the confusing part is that ALL of that happens on a page refresh only. On a normal load the state is not empty is loaded and in time for the UI to render it (no race condition), only, it's loaded with the old data from a previous state shape

enter image description here

Here is the redux dev tools enter image description here



Solution 1:[1]

Your selector takes one argument, but you're passing it two. Change it to:

const singlemedia = useSelector(selectSingleMedia);

Second, your singlemedia is the state. There's no need to setMedia(singlemedia);.

Your useEffect should look like:

useEffect(() => { 
  if (mediaStatus === 'idle') {
    dispatch(fetchSingleMediaTitle(id));
  }
}, [dispatch, id, mediaStatus]);

Also, you should look into RTK Query, which would replace createAsyncThunk: https://redux-toolkit.js.org/tutorials/rtk-query

Edit per our discussion:

useEffect(() => { 
  dispatch(fetchSingleMediaTitle(id));
}, [dispatch, id]);

Solution 2:[2]

The problem is very simple, just remove the media internal state variable:

const [media, setMedia] = useState([]); 

As what you are doing now is that you send the async request here:

if (mediaStatus === 'idle') {
    dispatch(fetchSingleMediaTitle(id)) //yes, it's imported
  }

And before the backend responds you read the store and set it in the internal state:

setMedia(singlemedia); // This is old data now, as we are waiting for the backend

If you wish to display the store state just use singlemedia in the render method.

If you wish to have some temporary state that mimics a backend response again use singlemedia and implement the temporary state in the redux store.

PS. the useEffect should depend only on id and useDispatch

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2