I've been following a tutorial for a while and ran into a wall trying to figure this out. Both figuratively and literally.
In a Dashboard
page, I try to use useSelector()
to get the state of the goalSlice
component. This component is very similar to the authSlice
component, which works just fine (I'll include for comparison to see if it helps get some answers.)
The issue lies within Dashboard.jsx:13
: "Uncaught TypeError: react_redux__WEBPACK_IMPORTED_MODULE_1__.useSelector(...) is undefined"
During runtime, useSelector()
will return undefined, and I can't figure out why.
I've tried using the answers from here, and I've also compared the reducers between goalSlice
and authSlice
. Nothing jumps out at me.
I get a hint from eslint, but I'm not understanding what caused it. goalSlice:73
:'state' is declared but its value is never read.
Dashboard.jsx
import {useEffect} from 'react'
import {useNavigate} from 'react-router-dom'
import {useSelector, useDispatch} from 'react-redux'
import GoalForm from '../components/GoalForm'
import Spinner from '../components/Spinner'
import {getGoals, reset} from '../features/goals/goalSlice'
function Dashboard() {
const navigate = useNavigate()
const dispatch = useDispatch()
const {user} = useSelector((state) => state.auth)
const { goals, isLoading, isError, message } = useSelector((state) => state.goals)
useEffect(() => {
if(isError) {
console.log(message)
}
if(!user) {
navigate('/login')
}
if (user) {
dispatch(getGoals());
}
return () => {
dispatch(reset());
}
}, [user, navigate, isError, message, dispatch])
return (
<>
<section className="heading">
<h1> Welcome {user.name} </h1>
<p>Goals Dashboard</p>
</section>
<GoalForm/>
</>
)
}
export default Dashboard
goalSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import goalService from './goalService'
const initialState = {
goals: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
}
// Create new goal
export const createGoal = createAsyncThunk(
'goals/create',
async (goalData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await goalService.createGoal(goalData, token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
// Get user goals
export const getGoals = createAsyncThunk(
'goals/getAll',
async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await goalService.getGoals(token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
// Delete user goal
export const deleteGoal = createAsyncThunk(
'goals/delete',
async (id, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await goalService.deleteGoal(id, token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
export const goalSlice = createSlice({
name: 'goals',
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
.addCase(createGoal.pending, (state) => {
state.isLoading = true
})
.addCase(createGoal.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.goals.push(action.payload)
})
.addCase(createGoal.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
})
.addCase(getGoals.pending, (state) => {
state.isLoading = true
})
.addCase(getGoals.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.goals = action.payload
})
.addCase(getGoals.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
})
.addCase(deleteGoal.pending, (state) => {
state.isLoading = true
})
.addCase(deleteGoal.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.goals = state.goals.filter(
(goal) => goal._id !== action.payload.id
)
})
.addCase(deleteGoal.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
})
},
})
export const { reset } = goalSlice.actions
export default goalSlice.reducer
authSlice.js
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit'
import authService from './authService'
const user = JSON.parse(localStorage.getItem('user'))
const initialState = {
user: user ? user : null,
isError: false,
isSuccess: false,
isLoading: false,
message: ''
}
export const registerUser = createAsyncThunk('auth/register', async (user, thunkAPI) => {
try {
return await authService.register(user)
} catch (error) {
const message = (error.response?.data?.message) || error.message || error.toString()
return thunkAPI.rejectWithValue(message)
}
})
export const loginUser = createAsyncThunk('auth/login', async (user, thunkAPI) => {
try {
return await authService.login(user)
} catch (error) {
const message = (error.response?.data?.message) || error.message || error.toString()
return thunkAPI.rejectWithValue(message)
}
})
export const logoutUser = createAsyncThunk('auth/logout', async () => {
await authService.logout()
})
export const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
reset: (state) => {
state.isError = false
state.isSuccess = false
state.isError = false
state.message = ''
}
},
extraReducers: (builder) => {
builder
.addCase(registerUser.pending, (state) => {
state.isLoading = true
})
.addCase(registerUser.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.user = action.payload
})
.addCase(registerUser.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
state.user = null
})
.addCase(loginUser.pending, (state) => {
state.isLoading = true
})
.addCase(loginUser.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.user = action.payload
})
.addCase(loginUser.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
state.user = null
})
.addCase(logoutUser.fulfilled, (state, action) => {
state.user = null
})
}
})
export const {reset } = authSlice.actions
export default authSlice.reducer
store.js
import { configureStore } from '@reduxjs/toolkit';
import authReducer from '../features/auth/authSlice';
import goalReducer from '../features/goals/goalSlice'
export const store = configureStore({
reducer: {
auth: authReducer,
goal: goalReducer
},
})
CodePudding user response:
It should be
const { goals, isLoading, isError, message } = useSelector((state) => state.goal)
since you have given in your store as goal