Reputation: 7739
I am following this great tut by Kent C. Dodds regarding usage React Context.
But I actually have this component nested within the provider so not sure why I am getting.
Error: userState must be used within a UserProvider
So perhaps I am not getting the point of creating a function which is throwing an Error despite following how you should use the provider... Perhaps I implemented it incorrectly?
So this is my userContext setup:
import React, { useState, useEffect, useContext, useReducer } from 'react';
var initialState = {
...state...
};
var UserStateContext = React.createContext();
var UserContextDispatch = React.createContext();
function setLocalStorage(key, value) {
...function innards...
}
function getLocalStorage(key, initialValue) {
...function innards...
}
function UserProvider({ children }) {
function userReducer(state, { type, payload }) {
switch (type) {
case 'setUserId': {
return { ...state, ...{ id: payload.id } };
}
case 'setAvatar': {
return {
...state,
...{ avatar: payload.avatar }
};
}
case 'setIsRoutingVisible': {
return {
...state,
...{ isRoutingVisible: payload.isRoutingVisible }
};
}
case 'addMarker': {
user.isLengthOfMarkersLessThanTwo
? {
...state,
markers: user.markers.concat(payload.marker)
}
: null;
break;
}
case 'setMap': {
return {
...state,
currentMap: payload.curerntMap
};
}
default: {
throw new Error(`Unhandled action type: ${type}`);
}
}
}
const [user, setUser] = useState(() => getLocalStorage('user', initialState));
var [state, dispatch] = useReducer(userReducer, user);
useEffect(() => {
setLocalStorage('user', state);
}, [state]);
return (
<UserStateContext.Provider value={state}>
<UserContextDispatch.Provider value={dispatch}>
{children}
</UserContextDispatch.Provider>
</UserStateContext.Provider>
);
}
function userState() {
const context = React.useContext(UserStateContext);
if (context === undefined) {
throw new Error('userState must be used within a UserProvider');
}
return context;
}
function userDispatch() {
const context = React.useContext(UserContextDispatch);
if (context === undefined) {
throw new Error('userDispatch must be used within a UserProvider');
}
return context;
}
export { UserProvider, userState, userDispatch };
The error is pointing to my Map component, which is passing state
and dispatch
props from UserContext
to my Routing component which is a class component.
import React, { useState, useContext, useEffect, useRef, useCallback } from 'react';
import { Button } from 'semantic-ui-react';
import L from 'leaflet';
import * as ELG from 'esri-leaflet-geocoder';
import { Map } from 'react-leaflet';
import { Dimmer, Loader } from 'semantic-ui-react';
import Routing from '../RoutingMachine/RoutingMachine.jsx';
import { userState, userDispatch } from '../Context/UserContext.jsx';
import UIContext from '../Context/UIContext.jsx';
import { stringify } from 'flatted';
export default function MyMap({}) {
var [zoom, setZoom] = useState(18);
var [animate, setAnimate] = useState(false);
var [userLocation, setUserLocation] = useState(null);
var mapRef = useRef();
console.log('userState() ', userState());
var {
avatar,
currentMap,
id,
isLengthOfMarkersLessThanTwo,
isRoutingVisible,
markers,
removeRoutingMachine
} = userState();
var dispatch = userDispatch();
var { isMobile, isDesktop } = useContext(UIContext);
useEffect(() => {
if (isRoutingVisible === false) {
dispatch({
type: 'setIsRoutingVisible',
payload: {
isRoutingVisible: true
}
});
}
});
useEffect(() => {
if (markers.length === 2) {
dispatch({
type: 'isLengthOfMarkersLessThanTwoFalse',
payload: { isLengthOfMarkersLessThanTwo: false }
});
}
}, [JSON.stringify(markers)]);
return (
<Map
animate={animate}
onLocationFound={handleOnLocationFound}
zoom={zoom}
ref={mapRef}
>
{isRoutingVisible && (
<Routing
markers={markers}
dispatch={dispatch}
removeRoutingMachine={removeRoutingMachine}
map={currentMap}
userLocation={userLocation}
isMobile={isMobile}
isDesktop={isDesktop}
/>
)}
</Map>
);
}
Upvotes: 1
Views: 11527
Reputation: 7739
It seemed the problem was the way I was passing the state
& dispatch
value into each provider:
return (
<UserStateContext.Provider value={state}>
<UserContextDispatch.Provider value={dispatch}>
{children}
</UserContextDispatch.Provider>
</UserStateContext.Provider>
);
I got it working by passing a object with the value: value={{ key: value }}
So one should do this:
return (
<UserStateContext.Provider value={{ state: state }}>
<UserContextDispatch.Provider value={{ dispatch: dispatch }}>
{children}
</UserContextDispatch.Provider>
</UserStateContext.Provider>
);
Upvotes: 2