In my Next.JS app, I'm trying to edit a user with useMutation hook from react-query
I'm not getting why formData returns undefined
Here's my UpdateForm component in which the update request gets triggered.
I'm using formik for handling form validation, and relative events (onChange, onBlur, ecc..)
useUser is a custom hook where I get the current user.
formId comes from redux, as soon as I click to the edit button, I get the ID of the relative user, and pass it to redux global state.
/*** components ***/
import {Button, InputGroup} from "../index";
import { useState } from "react";
/*** state ***/
import { useSelector } from "react-redux";
/*** Post Request ***/
import {editUser} from "../../../db/helpers/user-helper";
import { useQuery } from "react-query";
/*** form validation ***/
import {useFormik} from "formik";
import {editUserConfig} from "../../../utils/functions/validateInput";
import { validateInput } from "../../../utils/functions/validateInput";
import { getUser } from "../../../db/helpers/user-helper";
/*** custom hooks ***/
import useUserMutation from "../../../db/helpers/hooks/useUserMutation";
import useUser from "../../../db/helpers/hooks/useUser";
/*** icons ***/
import {BiLoaderCircle} from 'react-icons/bi'
//import { useEffect } from "react";
const UpdateForm = () => {
const {handleEditUser} = useUserMutation(editUser)
const notification = useSelector(state => state.notificationsState)
const {formId} = useSelector(state => state.toggleFormState)
const editConfig = useFormik({
initialValues: {
name: "",
email: ""
},
onSubmit: editUserRequest,
validate: validateInput
});
const editUserRequest = () => {
handleEditUser.mutate(formId, editConfig.values)
}
const {user} = useUser(formId, getUser)
return (
<>
{notification.notificationError &&
<div className="text-red-500 text-center font-semibold">{notification.notificationText}</div>
}
<form onSubmit={editConfig.handleSubmit}>
<InputGroup
label="Name"
name="name"
error={editConfig.errors.name}
isTouched={editConfig.touched.name}
{...editConfig.getFieldProps("name")}
/>
<InputGroup
label="Email"
name="email"
error={editConfig.errors.email}
isTouched={editConfig.touched.email}
{...editConfig.getFieldProps("email")}
/>
<Button disabled={!editConfig.values.name && !editConfig.values.email}
className={`w-full ${!editConfig.values.name || !editConfig.values.email ? 'bg-gray-200 text-gray-500' : 'bg-orange-200'}`}
type="submit"
>
{handleEditUser.isLoading ? <BiLoaderCircle size={14} /> : "Modifica"}
</Button>
</form>
</>
)
}
export default UpdateForm;
Here's the edit function from my useUserMutation custom hook, where I handle all the logic from useMutation hook for every request
import { useMutation, useQueryClient } from "react-query";
import { useDispatch } from "react-redux";
import { useRouter } from "next/router";
/*** actions ***/
import { successNotificationAction, closeNotificationAction, errorNotificationAction } from "../../../redux/reducers/notificationsReducer";
import { toggleFormAction } from "../../../redux/reducers/toggleFormReducer";
const useUserMutation = (requestFunction) => {
const dispatch = useDispatch()
const queryClient = useQueryClient()
const router = useRouter()
/// OTHER FUNCTIONS ///
const handleEditUser = useMutation(requestFunction, {
onSuccess: async (data) => {
dispatch(successNotificationAction("User Edited"))
setTimeout(() => dispatch(closeNotificationAction()), 1500)
dispatch(toggleFormAction())
},
onError: async () => {
dispatch(errorNotificationAction("Error Editing User"))
setTimeout(() => dispatch(closeNotificationAction()), 1500)
setTimeout(() => dispatch(toggleFormAction()), 1500)
}
});
return {handleDeleteUser, handlePostUser, handleEditUser}
}
export default useUserMutation;
This is my API endpoint from pages/api/users/[userId].js
/*** PUT http://localhost:3000/api/users/id ***/
export async function updateUser(req, res) {
try {
const {userId} = req.body /* OR ? */ req.query
const {formData} = req.body
if(userId && formData) {
const updatedUser = await AddedUser.findByIdAndUpdate(userId, formData)
res.status(200).json({message: "User Updated!", user: updatedUser})
}
res.status(404).json({error: "User not selected..."})
} catch(error) {
res.status(404).json({error: "Error updating data"})
}
}
Finally this is my axios request function, the console.log returns undefined form formData, the ID is logged anyway.
The real request is commented cause it throws an error, precisely the one that is set in the API endpoint res.status(404).json({error: "User not selected..."})
export const editUser = async (userId, formData) => {
console.log("data from user-helper.js", userId, formData)
//const response = await axios.put(`${BASE_URL}api/users/${userId}`, formData)
//console.log(response)
//return response
}
Why I'm not getting the data inserted in the form? Is something missing?
Related
I am building an app using next13 (to make use of server side components), however, for some reason my existing AuthContext is not working. I am getting the following error:
TypeError: React.createContext is not a function
From what I can see, the AuthContext needs to be set to 'use client', as there is use of useState and useEffect within it, but for some reason the application no longer recognises that createContext is actually a function.
This is my AuthContext:
'use client';
import { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, signOut, signInWithEmailAndPassword, createUserWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../config';
const AuthContext = createContext({});
export const useAuth = () => useContext(AuthContext);
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setLoading(true);
setUser(user ?? null);
setLoading(false);
});
return () => unsubscribe();
}, []);
const login = async (email, password) => {
await signInWithEmailAndPassword(auth, email, password);
};
const logout = async () => {
setUser(null);
await signOut(auth)
};
const register = async (email, password) => {
try {
const userCred = await createUserWithEmailAndPassword(auth, email, password);
await userCred.user.sendEmailVerification({
url: process.env.NEXT_PUBLIC_HOST
});
} catch (err) {
return {
errorCode,
errorMessage
}
}
};
return (
<AuthContext.Provider value={{ user, loading, login, logout, register }}>
{children}
</AuthContext.Provider>
);
};
The AuthContext is then used in my main layout page within the app directory:
'use client';
import { CssBaseline, Container } from '#mui/material';
import { NavBar, Footer } from '../components';
import { AuthContextProvider } from '../context';
import '#fontsource/roboto/300.css';
import '#fontsource/roboto/400.css';
import '#fontsource/roboto/500.css';
import '#fontsource/roboto/700.css';
const RootLayout = ({ children }) => {
return (
<html lang='en'>
<head>
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<AuthContextProvider>
<CssBaseline />
<NavBar />
<Container component='main' sx={{ padding: 3 }}>
{children}
</Container>
<Footer />
</AuthContextProvider>
</body>
</html>
);
}
export default RootLayout;
I am unsure if I need to take a different approach to authentication, perhaps using the next-auth package, but I am not sure what the best way would be.
Cheers for any help!
Here's an example of useContext I am using on my application.
'use client'
import { createContext, useContext, useEffect, useState } from 'react'
import { getAuth, User } from 'firebase/auth'
import { initializeApp, getApps, getApp } from 'firebase/app'
import nookies from 'nookies'
const firebaseConfig = {
...
}
getApps().length ? getApp() : initializeApp(firebaseConfig)
const auth = getAuth()
const AuthContext = createContext<User | null>(null)
export function AuthProvider({ children }: any) {
//
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
return auth.onIdTokenChanged(async (user) => {
if (!user) {
setUser(null)
nookies.set(undefined, 'token', '', { path: '/' })
} else {
const token = await user.getIdToken()
setUser(user)
nookies.set(undefined, 'token', token, { path: '/' })
}
})
}, [])
useEffect(() => {
const handle = setInterval(async () => {
const user = auth.currentUser
if (user) await user.getIdToken(true)
}, 15 * 60 * 1000)
return () => clearInterval(handle)
}, [])
return <AuthContext.Provider value={user}>{children}</AuthContext.Provider>
}
export const useAuth = () => {
return useContext(AuthContext)
}
Note that we're also forcing token refresh every 15 minutes, and saving that to cookies. You can access cookies in server pages using the new next13 cookies package.
You can also get the user by importing the useAuth hook we just created.
For example
'use client'
import useAuth from '../context/AuthProvider'
const Page = () => {
const {user} = useAuth()
// Rest of your application
}
Hope it helps
I'm in the process of building a merch e-commerce website for a client utilizing the commerce.js API however I've run into a problem. When passing the "cart" object as a prop to the checkout file it returns as an empty object which breaks the website. The web application passes the "cart" object as a prop in other parts of the code and works just fine. Is there something I'm doing wrong?
Code for reference:
import React, { useState, useEffect } from 'react';
import {Paper, Stepper, Step, StepLabel, Typography, CircularProgress, Divider, Button} from '#material-ui/core';
import { commerce } from '../../../lib/commerce';
import Addressform from '../Addressform';
import Paymentform from '../Paymentform';
const steps =['Shipping Address', 'Payment details'];
const Checkout = ({ cart }) => {
const [activeStep, setActiveStep] = useState(0);
const [checkoutToken, setCheckoutToken] = useState(null);
useEffect (() => {
const generateToken = async () => {
console.log(cart.id);
// returns as undefined
try {
const token = await commerce.checkout.generateToken(cart.id, { type: 'cart' });
console.log(token);
setCheckoutToken(token);
console.log("Success!")
} catch (error) {
console.log(error); //Returns 404 Error Obv
console.log("Didnt work")
}
}
generateToken();
}, []);
const Confirmation = () => (
<>
Confirmation
</>
);
const Form = () => activeStep === 0
? <Addressform />
: < Paymentform />
return(
<>
...
</>
);
};
export default Checkout;
I'm using https://github.com/signavio/react-mentions for React Mentions, a facebook #mentions-like frontend feature where a user can mention another user in the comment box.
Below is the code that gets data through fetch but couldn't get the data using redux and useSelector.
I used react-mentions and react-redux packages
Using fetchUsers query I can get the data from github and pass them to Mention component for '#' suggestions.
Now how to use redux instead of fetch to display suggestions for '#'? i.e, how to use callback passed to the getData function to get the hashtag suggestions?
import { useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { MentionsInput, Mention } from 'react-mentions';
import {
fetchHashTags,
} from './redux/tagsSlice';
function fetchUsers(query, callback) {
if (!query) return
fetch(`https://api.github.com/search/users?q=${query}`, { json: true })
.then(res => res.json())
// Transform the users to what react-mentions expects
.then(res =>
res.items.map(user => ({ display: user.login, id: user.login }))
)
.then(callback)
}
function AsyncGithubUserMentions({ value, data, onChange }) {
const dispatch = useDispatch();
const users = useSelector(
(state) => state?.tags?.users,
shallowEqual
);
const getData = (query, callback) => {
dispatch(fetchHashTags(query));
callback(users);
}
return (
<div className="async">
<h3>Async Github user mentions</h3>
<MentionsInput
value={value}
onChange={onChange}
placeholder="Mention any Github user by typing `#` followed by at least one char"
a11ySuggestionsListLabel={"Suggested Github users for mention"}
>
<Mention
displayTransform={login => `#${login}`}
trigger="#"
data={fetchUsers}
/>
<Mention
displayTransform={login => `#${login}`}
trigger="#"
data={getData}
/>
</MentionsInput>
</div>
)
}
export default AsyncGithubUserMentions
Redux code to fetch the hashtags from github:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import axios from 'axios';
export const fetchHashTags = createAsyncThunk(
'tags/hash',
async (value, { rejectWithValue, getState, dispatch }) => {
try {
const { data } = await axios.get(
`https://api.github.com/search/users?q=${query}`,
);
return data;
} catch (error) {
throw error;
}
}
);
const tagsSlice = createSlice({
name: 'tags',
initialState: {},
extraReducers: (builder) => {
builder.addCase(fetchHashTags.fulfilled, (state, action) => {
state.usertags = action?.payload;
});
},
});
export default tagsSlice.reducer;
I have a custom hook
import { useState } from "react";
import { useDispatch } from "react-redux";
import axios from "axios";
import getDevices from "../actions/devicesAtions";
import { isPositiveInteger, FORM_FIELDS } from "../helper";
export default function useDevice(value) {
const dispatch = useDispatch();
const [device, setDevice] = useState(value);
const [msg, setMsg] = useState("");
const saveDeviceChange = ({ target }) => {
const { name, value } = target;
setDevice({
...device,
[name]: value
});
setMsg("");
};
const saveDeviceSubmit = async (
e,
axiosMethod,
selectedUrl,
newDevice = device
) => {
e.preventDefault();
const { system_name, type, hdd_capacity } = device;
if (!system_name || !type || !hdd_capacity) {
setMsg(
`Please fill out ${!system_name ? FORM_FIELDS.SYS_NAME : ""} ${
!type ? FORM_FIELDS.DEVICE_TYPE : ""
} ${!hdd_capacity ? FORM_FIELDS.HDD_CAPACITY : ""}!`
);
return false;
}
if (!isPositiveInteger(hdd_capacity)) {
setMsg(
"Please enter a positive number or round it to the nearst whole number!"
);
return false;
}
try {
await axios({
method: axiosMethod,
url: selectedUrl,
data: device,
headers: {
"Content-Type": "application/json"
}
});
dispatch(getDevices());
} catch (err) {
console.log(err);
}
setMsg("Changes have been made!");
setDevice(newDevice);
};
return {
device,
setDevice,
msg,
setMsg,
saveDeviceChange,
saveDeviceSubmit
};
}
The EditDeviceWrapper component uses the states and functions from the custom hook. When this component renders, the selectedDevice is assigned as the value for the device that's from the custom hook. When first rendered, the values are displayed correctly on the from. However, after I clicked refresh, the device state from the custom hook disappear while the selectedDevice state from the redux still exits. How to maintain the device state from the custom hook after refreshing the the component?
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useParams } from "react-router-dom";
import ReusedForm from "./ReusedForm";
import useDevice from "./useDevice";
import { getDeviceDetails } from "../actions/devicesAtions";
export default function EditDeviceWrapper() {
const dispatch = useDispatch();
const { selectedDevice } = useSelector((state) => state.allDevices);
const { id } = useParams();
useEffect(() => {
dispatch(getDeviceDetails(id));
}, [id, dispatch]);
const { device, msg, saveDeviceChange, saveDeviceSubmit } = useDevice(
selectedDevice
);
console.log(device, selectedDevice);
return (
<>
<p className="form-msg">{msg}</p>
<ReusedForm
saveDeviceSubmit={(e) =>
saveDeviceSubmit(e, "put", `http://localhost:3000/devices/${id}`)
}
selectDeviceValChange={saveDeviceChange}
heading="Update Device"
system_name={device.system_name}
type={device.type}
hdd_capacity={device.hdd_capacity}
/>
<p>{id}</p>
</>
);
}
May be persist the state after page refresh this link helps you to resolve your problem.
I have made login and logout actions and userReducer. How can I integrate AsyncStorage with Redux? I am using Redux Thunk as a middleware.
I am able to implement login and logout using internal state variable but I am not able to understand how to break it down into action and reducer as well as make use of AsyncStorage for storing accessToken.
Original Code:
_onLogin = () => {
auth0.webAuth
.authorize({
scope: 'openid profile',
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.setState({ accessToken: credentials.accessToken });
})
.catch(error => console.log(error));
};
_onLogout = () => {
if (Platform.OS === 'android') {
this.setState({ accessToken: null });
} else {
auth0.webAuth
.clearSession({})
.then(success => {
this.setState({ accessToken: null });
})
.catch(error => console.log(error));
}
};
loginAction.js:
import { LOGIN_USER } from './types';
import Auth0 from 'react-native-auth0';
var credentials = require('./auth0-credentials');
const auth0 = new Auth0(credentials);
export const loginUser = () => dispatch => {
auth0.webAuth
.authorize({
scope: 'openid profile',
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials =>
dispatch({
type: LOGIN_USER,
payload: credentials.accessToken
})
)
.catch(error => console.log(error));
}
logoutAction.js:
import { LOGOUT_USER } from './types';
import Auth0 from 'react-native-auth0';
var credentials = require('./auth0-credentials');
const auth0 = new Auth0(credentials);
export const logoutUser = () => dispatch => {
auth0.webAuth
.clearSession({})
.then(success =>
dispatch({
type: LOGOUT_USER,
payload: null
})
)
.catch(error => console.log(error));
}
userReducer.js:
import { LOGIN_USER, LOGOUT_USER } from '../actions/types';
const initialState = {
accessToken: null
}
export default function (state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
_storeData = async () => {
try {
await AsyncStorage.setItem('accessToken', action.payload);
} catch (error) {
console.log(error)
}
}
return {
...state,
accessToken:action.payload
};
case LOGOUT_USER:
_removeData = async (accessToken) => {
try {
await AsyncStorage.removeItem(accessToken);
} catch (error) {
console.log(error)
}
}
return {
...state,
accessToken:action.payload
};
default:
return state;
}
}
I am new to Redux so I tried converting original code into actions and reducers but I am not sure whether I have implemented AsyncStorage in userReducer.js correctly?
To persist redux state I recommend you redux-persist.
Installation:
npm i -S redux-persist
Usage:
First, configure redux store
// configureStore.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web and AsyncStorage for react-native
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}
Then, wrap your root component with PersistGate
import { PersistGate } from 'redux-persist/integration/react'
// ... normal setup, create store and persistor, import components etc.
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
);
};
You can conveniently use AsyncStorage alone OR redux to manage authentication state. Depends on which you are comfortable with. I will give you an example of both.
For AsyncStorage:
Assuming you have authentication keys that is valid for 2 weeks only. You can take note when your user logs in and save the time. eg:
//LoginScreen
import { onSignIn } from '../actions/auth'; //I will describe the onSignInMethod below
import axios from 'axios'; //lets use axios. You may use fetch too.
export default class LoginScreen extends Component {
//your code: state, static etc
loginMethod = () => {
const url = yourauthUrl;
const payload = {
email: this.state.email,
password: this.state.password
};
axios.post(url, payload)
.then((response) => {
if (response.status == 200) {
const dateOfLastLogin = new Date().getTime().toString(); //take note of the time the user logs in.
AsyncStorage.setItem('dateOfLastLogin', dateOfLastLogin);
}
})
.then(() => {
onSignIn() //onSignIn handles your sign in. See below.
.then(() => this.props.navigation.navigate('AfterSignInPage'));
})
.catch(() => { // your callback if onSignIn Fails
});
})
.catch((error) => { //your callback if axios fails
});
}
}
In ../actions/auth.js
import { AsyncStorage } from 'react-native';
export const onSignIn = () => AsyncStorage.setItem('auth_key', 'true');
//in LoginScreen we called this to set that a user has successfully logged in
//why is true a string? -- Because Asyncstorage stores only strings
export const onSignOut = () => AsyncStorage.multiRemove(['auth_key', 'dateOfLastLogin']);
//now lets create a method that checks if the user is logged in anytime
export const isSignedIn = () => {
return new Promise((resolve, reject) => {
AsyncStorage.multiGet(['auth_key', 'dateOfLastLogin'])
.then((res) => {
const userKey = res[0][1];
const lastLoginDate = parseInt(res[1][1]);
const today = new Date().getTime();
const daysElapsed = Math.round(
(today - lastLoginDate) / 86400000
);
if (userKey !== null && (daysElapsed < 14)) {
resolve(true);
} else {
resolve(false);
}
})
.catch((err) => reject(err));
});
};
now we can import { isSignedIn } from '../actions/auth'; from any of our components and use it like this:
isSignedIn()
.then((res) => {
if (res) {
// user is properly logged in and the login keys are valid and less than 14 days
}
})
////////////////////////////////////////////////////////////////////////////
If you want to use redux
Handling login in redux
In your types.js
//types.js
export const LOGGED_IN = 'LOGGED_IN';
In your redux actions
//loginActions.js
import {
LOGGED_IN,
} from './types';
export function login() {
let dateOfLastLogin = null;
let isLoggedIn = 'false';
AsyncStorage.multiGet(['auth_key', 'dateOfLastLogin'])
.then((res) => {
isLoggedIn = res[0][1];
dateOfLastLogin = parseInt(res[1][1]);
}); //note this works asynchronously so, this may not be a good approach
return {
type: LOGGED_IN,
isLoggedIn,
dateOfLastLogin
};
}
In your loginReducer
//LoginReducer.js
import {
LOGGED_IN
} from '../actions/types';
const initialState = {
userIsLoggedIn: false
};
export function loginReducer(state=initialState, action) {
switch (action.type) {
case LOGGED_IN:
const userKey = action.isLoggedIn;
const lastLoginDate = action.dateOfLastLogin;
const today = new Date().getTime();
const daysElapsed = Math.round(
(today - lastLoginDate) / 86400000
);
let trulyLoggedIn = false;
if (userKey !== null && (daysElapsed < 14)) {
trulyLoggedIn = true;
} else { trulyLoggedIn = false }
return {
userIsLoggedIn: trulyLoggedIn
};
default:
return state;
}
}
In your ./reducers/index.js
//reducers index.js
import { combineReducers } from 'redux';
import { loginReducer } from './LoginReducers';
const rootReducer = combineReducers({
loggedIn: loginReducer
});
export default rootReducer;
In your store where you used redux-thunk, applyMiddleWare. Lets call it configureStore.js
//configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
In your App.js
//App.js
import { Provider } from 'react-redux';
import configureStore from './src/store/configureStore'; //where you configured your store
import { YourMainNavigator } from '../src/config/router'; //where your root navigator is
const store = configureStore();
export default class App extends Component<{}> {
render() {
return (
<Provider store={store}>
<YourMainNavigator />
</Provider>
);
}
}
You should know you no longer need the isSignedIn method in your auth.js
Your login method remains the same as outlined above in LoginScreen.
Now you can use redux to check the state of login like this:
import React, {Component} from 'react';
import {connect} from 'react-redux';
class MyComponent extends Component {
someFunction() {
if (this.props.loggedIn) {
//do something
}
}
}
const mapStateToProps = (state) => {
return {
loggedIn: state.loggedIn.userIsLoggedIn
};
}
export default connect(mapStateToProps)(MyComponent);
There should be a better way of using redux to manage login - better than what I outlined here. I think you can also use redux to manage your login state without using AsyncStorage. All you need to do is in your loginScreen, if the login functions returns a response.status == 'ok', you can dispatch an action to redux that logs the user in. In the example above, using asyncstorage you might only need to use redux to check if a user is logged in.
It is recommended that you use an abstraction on top of AsyncStorage instead of AsyncStorage directly for anything more than light usage since it operates globally. Redux-persist is that abstraction that goes on top of AsyncStorage. It provides a better way to store and retrieve more complex data (e.g. redux-persist has persistReducer(), persistStore()).
React native typescript implementation
storage.ts
import AsyncStorage from "#react-native-community/async-storage";
import { createStore, combineReducers } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import exampleReducer from "./example.reducer";
const rootReducer = combineReducers({
example: exampleReducer,
});
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["example"],
};
// Middleware: Redux Persist Persisted Reducer
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer);
// Middleware: Redux Persist Persister
let persistor = persistStore(store);
export { store, persistor };
App.tsx
import React from "react";
import { PersistGate } from "redux-persist/es/integration/react";
import { Provider } from "react-redux";
import RootNavigator from "./navigation/RootNavigator";
import { store, persistor } from "./store";
function App() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootNavigator />
</PersistGate>
</Provider>
);
}
export default App;