I'm building an expo-app and I manage state with react-redux
, so my <Provider>
in is in the top level _layout file.
My problem is I'm working with expo notification and I need to get the title, body, and some data received from the server through the notification and dispatch it to the store for later use, but when I try to dispatch I got an error that says:
could not find react-redux context value
I realize that this error occurs because I try to use dispatch before wrapping the whole app in redux <Provider>
but I don't know how I can do this. Below is my code.
import React from "react";
import { Stack, SplashScreen } from "expo-router";
import { useEffect } from "react";
import { useFonts } from "expo-font";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { Provider } from "react-redux";
import { persistor, store } from "../src/redux/store/store";
import { PersistGate } from "redux-persist/integration/react";
import * as Notifications from "expo-notifications";
import { router } from "expo-router";
import { useDispatch } from "react-redux";
import { addMessage } from "../src/redux/slice/system-messages";
export {
// Catch any errors thrown by the Layout component.
ErrorBoundary,
} from "expo-router";
export const unstable_settings = {
// Ensure that reloading on `/modal` keeps a back button present.
initialRouteName: "(tabs)",
};
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
function saveNotificationResponse() {
const dispatcher = useDispatch();
const subscription = Notifications.addNotificationResponseReceivedListener(
(response) => {
console.log(response.notification.request.content.data);
dispatcher(addMessage(response.notification.request.content.data));
}
);
return () => {
subscription.remove();
};
}
export default function _layout() {
useEffect(() => {
saveNotificationResponse();
}, []);
const [loaded, error] = useFonts({
SpaceMono: require("../src/assets/fonts/SpaceMono-Regular.ttf"),
DMBold: require("../src/assets/fonts/DMSans-Bold.ttf"),
DMMedium: require("../src/assets/fonts/DMSans-Medium.ttf"),
DMRegular: require("../src/assets/fonts/DMSans-Regular.ttf"),
...FontAwesome.font,
});
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
useEffect(() => {
if (error) throw error;
}, [error]);
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Stack>
<Stack.Screen
name="index"
options={{
title: "Home",
}}
/>
<Stack.Screen
name="[missing]"
options={{
title: "404",
}}
/>
<Stack.Screen
name="(tabs)"
options={{
headerShown: false,
}}
/>
<Stack.Screen
name="order/index"
options={{
headerShown: false,
}}
/>
<Stack.Screen
name="search/searchpage"
options={{
headerShown: false,
}}
/>
</Stack>
</PersistGate>
</Provider>
);
}
So when I try to run saveNotificationResponse()
I got this error how can I fix this and still save my response to the store? thanks
You cannot use the useDispatch
from a non-React function or custom hook, e.g. it can't be called from within the saveNotificationResponse
function.
You can use the store
object to directly dispatch actions to the store.
...
import { ..., store } from "../src/redux/store/store";
...
function setupNotificationListener() {
const subscription = Notifications.addNotificationResponseReceivedListener(
(response) => {
store.dispatch(addMessage(
response.notification.request.content.data
));
}
);
// Return the subscription so callees can unsubscribe when they need to
return subscription;
}
...
export default function _layout() {
useEffect(() => {
const subscription = setupNotificationListener();
return () => {
subscription.remove();
};
}, []);
The alternative is to create a React component that is rendered under the Redux Provider
component such that it can access the Redux context.
Example:
const NotificationListener = () => {
const dispatch = useDispatch();
useEffect(() => {
const subscription = Notifications.addNotificationResponseReceivedListener(
(response) => {
dispatch(addMessage(response.notification.request.content.data));
}
);
return subscription.remove;
}, [dispatch]);
return null;
};
export default function _layout() {
const [loaded, error] = useFonts({
...
});
...
if (!loaded) {
return null;
}
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<NotificationListener />
<Stack>
...
</Stack>
</PersistGate>
</Provider>
);
}