'Redux useSelector returns nothing in child component
I am new to React Native and Redux, and was hoping someone could help out in my issue? I have a parent component that fetches some user data (their location) and dispatches to a redux store:
Parent
import { useDispatch } from 'react-redux'
import { setLocation } from './store/locationSlice'
const App = () => {
const dispatch = useDispatch()
const getLocation = () => {
const location = await fetchLoc()
dispatch(setLocation(location))
}
useEffect(() => {
getLocation()
},[])
}
My child component is intended to retrieve this data using the useSelector hook
Child
import { useSelector } from 'react-redux'
const HomeScreen = () => {
const location = useSelector(state => state.location)
useEffect(() => {
if (location) {
getEntitiesBasedOnLocation(location)
}
},[location])
}
However, in my case, useSelector never retrieves the up-to-date information that i have dispatched in the parent, with location returning undefined. I'm fairly certain there's a simple oversight here, but i'm at a loss as to what this could be. I was under the impression that useSelector subscribes to state changes, so why is it that that my dispatched action that causes a change of state is ignored? Using my debugger, I can see that my state is definitely updated with the correct data, but the child component doesn't pick this up..
Here's my location slice:
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
location: {
id: null,
name: null,
latitude: null,
longitude: null
}
}
const locationSlice = createSlice({
name: 'location',
initialState,
reducers: {
setLocation: (state, action) => {
const { id, name, latitude, longitude } = action.payload
state.location = { id, name, latitude, longitude }
}
}
})
export const { setLocation } = locationSlice.actions
export default locationSlice.reducer
UPDATE The store is configured by wrapping the App.js component in a Provider component, with the store passed as its props as follows:
Root.js
import { configureStore } from '@reduxjs/toolkit'
import { Provider } from 'react-redux'
import locationReducer from './src/store/locationSlice'
import App from './src/App'
const Root = () => {
const store = configureStore({ reducer: locationReducer })
return (
<Provider store={store)>
<App />
</Provider>
)
}
Solution 1:[1]
The issue is in your selector. You've created the slice called 'location' and within that slice you've got your state { location: {...}}. So from the perspective of the selector (which accesses your global state, not just the location slice) the path to your data would be state.location.location. But your selector is trying to read out of state.location which only has a location prop. Anything else you tried to read out would be undefined.
Solution 2:[2]
It is common to export a custom selection function from the slice configuration. Remember that the selector must take exactly the data that you want to share in your component tree (locationSlice.state.location in this case). This is not mandatory, it is just to facilitate development.
// locationSlice
import { createSlice } from '@reduxjs/toolkit'
//...
export const { setLocation } = locationSlice.actions
export const selectLocation = (state) => state.location.location
export default locationSlice.reducer
// Child
import { useSelector } from 'react-redux'
import {selectLocation} from './src/store/locationSlice'
const HomeScreen = () => {
const location = useSelector(selectLocation)
//...
}
Solution 3:[3]
My workaround was to move my getLocation() function in the parent to the child component. useSelector now gets the state as expected. I feel that this work-around defeats the object of having global state access though, and i could probably just use local state rather than Redux.
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 | Chad S. |
| Solution 2 | Arlle Brasil |
| Solution 3 | martyno |
