'Passing the state from a Redux Slice to the Component is giving me undefined
I'm doing this in other components very similarly, except this one takes a parameter of title_id. I am getting my data to fetch when I check the Redux DevTool and I console logged the payload from the mediaSlice too, but the useSelector in my Component is bringing nothing in and not updating my state. Can you help me figure out what exactly is it that I am messing up on so my selector can send the payload to my Component's state?
I made comments/questions in my code below where I think my problem areas are.
Here is my mediaSlice.js
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 SINGLE_MEDIA_API = `${BASE_URL}/titlestest`
const initialState = {
media:[],
status: 'idle',
error:null
}
export const fetchSingleMediaTitle = createAsyncThunk(
'medias/fetchSingleMediaTitle',
async (mediaId) => { // <-- would this be where I declare the param key I am using?
try {
const response = await axios.get(
SINGLE_MEDIA_API,
{
headers: {
'Content-Type': 'application/json',
'X-API-KEY': KEY,
},
params: {
titleId: mediaId,
}
}
)
return response.data.Item;
} catch (error) {
console.error('API call error:', error.message);
}
}
)
const mediaSlice = createSlice({
name: 'medias',
initialState,
reducers:{},
extraReducers(builder) {
builder
.addCase(fetchSingleMediaTitle.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchSingleMediaTitle.fulfilled, (state, action) => {
state.status = 'succeeded'
const loadedMedia = action.payload
state.media = loadedMedia
console.log("loadedMedia: ", loadedMedia); // this is successfully printing the data object
})
.addCase(fetchSingleMediaTitle.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
}
})
// SELECTORS
export const selectSingleMedia = (state, mediaId) => state.media.media.find(item => item.title_id === mediaId); //this is where I am suspecting the problem is in
export const getMediaStatus = (state) => state.media.status;
export const getMediaError = (state) => state.media.error;
export default mediaSlice.reducer
And my Component. Reduced for brevity
import React, { useState, useEffect, useRef, Fragment } from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { getMediaError, getMediaStatus, selectSingleMedia, fetchSingleMediaTitle } from '../../features/medias/mediaSlice'
import { useParams, useHistory } from "react-router-dom";
const SingleMediaTitle = () => {
//STATES
const [data, setData] = useState([]);
const {mediaId} = useParams();
const dispatch = useDispatch();
const media = useSelector((state, mediaId) => selectSingleMedia(state, mediaId)) //This is a suspect line too
const mediaStatus = useSelector(getMediaStatus)
const error = useSelector(getMediaError)
useEffect(() => {
if (mediaStatus === 'idle') {
dispatch(fetchSingleMediaTitle(mediaId))
}
setData(media);
console.log("media: ", media); // This is undefined
}, [mediaId, media, mediaStatus, dispatch])
let content;
if (mediaStatus === 'loading') {
content = <Box className="loading">
<Typography variant="subtitle2">Loading ..</Typography>
</Box>
} else if (mediaStatus === 'succeeded') {
content = <Box>
{media.content_source} //Though redux succeeded, this is not displaying
<Box/>
} else if (mediaStatus === 'failed') {
content = <Box className="loading">
<Typography variant="subtitle2">{error}</Typography>
</Box>
}
return (
<Box sx={{p:3, pt:9}}> {content} </Box>
)
}
export default SingleMediaTitle
I really appreciate your help. Being the only front end dev in house is tough lol
Solution 1:[1]
I can spot 2 things that can cause the problem:
1) Your selectors
Since useSelector will get the entire state, you have to dig into your slice first and then deeper into the media data. Since the name of slice is medias this should be the first one. According to this your selectors should be:
// SELECTORS
export const selectSingleMedia = (state, mediaId) => state.medias.media.find(item => item.title_id === mediaId);
export const getMediaStatus = (state) => state.medias.media.status;
export const getMediaError = (state) => state.medias.media.error;
2) The single media selector
const {mediaId} = useParams();
const media = useSelector((state, mediaId) => selectSingleMedia(state, mediaId))
Since mediaId exists in the scope of the selector you should not expect it as an argument of the selector and just pass it as parameter to selectSingleMedia like below:
const {mediaId} = useParams();
const media = useSelector((state) => selectSingleMedia(state, mediaId))
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 | fgkolf |
