Using react with cloud firestore - javascript

I am trying to figure out how to get cloud firestore working with a react app.
I have found this tutorial, which uses react with realtime database, and have gotten it to load. Now I'm trying to figure out what changes I need to make to get it working with cloud firestore.
In my firebase.js, I have:
import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import firestore from "firebase/firestore";
class Firebase {
constructor() {
app.initializeApp(config).firestore();
this.auth = app.auth();
// this.db = app.firebase.database()
this.db = app.firebase.firestore();
}
doCreateUserWithEmailAndPassword = (email, password) =>
this.auth.createUserWithEmailAndPassword(email, password);
doSignInWithEmailAndPassword = (email, password) =>
this.auth.signInWithEmailAndPassword(email, password);
doSignOut = () =>
this.auth.signOut();
doPasswordReset = email =>
this.auth.sendPasswordResetEmail(email);
doPasswordUpdate = password =>
this.auth.currentUser.updatePassword(password);
// *** User API ***
user = uid => this.db.ref(`users/${uid}`);
users = () => this.db.ref('users');
}
export default Firebase;
Then, in my form, I'm trying this:
import { withFirebase } from '../../../components/firebase';
onSubmit = event => {
const { username, email, passwordOne } = this.state;
this.props.firebase
.doCreateUserWithEmailAndPassword(email, passwordOne)
.then(authUser => {
// Create a user
return this.props.firebase
.user(authUser.user.uid)
.set({
username,
email,
});
})
.then(authUser => {
this.setState({ ...INITIAL_STATE });
this.props.history.push(ROUTES.INITIAL_PROFILE);
})
.catch(error => {
this.setState({ error });
});
event.preventDefault();
}
onChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
The import withFirebase has:
import FirebaseContext, { withFirebase } from './Context';
import Firebase from '../../firebase.1';
export default Firebase;
export { FirebaseContext, withFirebase };
FirebaseContext has:
import React from 'react';
const FirebaseContext = React.createContext(null);
export const withFirebase = Component => props => (
<FirebaseContext.Consumer>
{firebase => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
);
export default FirebaseContext;
When I try this, I get an error that says:
TypeError: Cannot read property 'firestore' of undefined
The error message points to this line of the firebase config file:
this.db = app.firebase.firestore();
How can I get firestore working in a react app?

Change this:
this.db = app.firebase.firestore();
into this:
this.db = app.firestore();
initializeApp() returns type App, and inside App you can find the method firestore():
https://firebase.google.com/docs/reference/js/firebase.app.App.html#firestore

Related

How to edit a user with useMutation

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?

How to properly use firebase.initializeApp using React?

I set up my React project to use firebase auth using the modular v9 SDK like so. I now would like to create other hooks like useAnalytics, useFirestore, etc, that will allow me to access those utilities in components that need them. But if I use this same pattern for other firebase services, I will end up having to wrap my app with several contexts.
So instead of this auth provider component, I'm thinking of replacing it with a FirebaseProvider component that will wrap everything, but I am not sure if that is correct, or how I would integrate it with the existing auth code below.
import React, {useState, useContext, useEffect, createContext } from "react"
import {
getAuth,
signOut,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
} from "firebase/auth";
// I need to move this elsewhere like index.js
import { initializeApp } from "firebase/app";
firebase.initializeApp(<app config object>);
const auth = getAuth();
const authContext = createContext();
export const useAuth = () => {
return useContext()
}
// my App component is wrapped with this JSX element
export const ProvideAuth = ({children}) => {
const auth = useProvideAuth();
return <authContext.Provider value={auth}></authContext.Provider>
}
const useProvideAuth = () => {
const [user, setUser] = useState(null)
const signIn = (email, password) => {
signInWithEmailAndPassword(auth, email, password).then((res) => {
setUser(res.user)
})
}
const createUser = ...
const signOut = ...;
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
// remove listener on unmount
return () => unsubscribe();
}, []);
return {user, signIn, signOut, createUser};
}
I tried placing initializeApp in the index.js, but that code never seems to run and causes the authentication to fail.

produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'

I got following error at onAuthStateChanged method at store.dispatch(userActions.setUser(null));
Error: [Immer] produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '[object Object]'
I am trying to change to firebase authentication from jwt. So custom jwt authentication is using redux. Now when I call firebase's signOut(auth) method, onAuthStateChanged method give me this error. May I know how to write immerable object for user.
user_reducer.js
import produce from "immer";
import * as Action_Constants from "../actions/user_actions";
const initialState = null;
export const UserReducer = produce((state = initialState, action) => {
switch (action.type) {
case Action_Constants.SET_USER: {
return action.user;
}
case Action_Constants.FETCH_USER_COMPLETED: {
return action.user;
}
default:
return state;
}
});
user_actions.js
export const FETCH_USER = "FETCH_USER";
export const FETCH_USER_COMPLETED = "FETCH_USER_COMPLETED";
export const SET_USER = "SET_USER";
export const actionCreators = {
fetchUser: (id) => ({
type: FETCH_USER,
id,
}),
fetchUserCompleted: (user) => ({
type: FETCH_USER_COMPLETED,
user,
}),
setUser: (user) => ({
type: SET_USER,
user,
}),
};
I have deleted other firebase functions to simply the file.
auth_provider.jsx
import React, { useState, useEffect, useContext, createContext } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { signIn, signUp } from "../helpers/gql_auth_helpers";
import paths from "../routes/paths";
import { store } from "../store/configure_store";
import { actionCreators as userActions } from "../store/actions/user_actions";
import { auth } from "../helpers/init-firebase";
import {
onAuthStateChanged,
signOut,
} from "firebase/auth";
const AuthContext = createContext(null);
let accessToken = "";
export const getAccessToken = () => accessToken;
export const setAccessToken = (token) => {
accessToken = token;
};
export const AuthProvider = ({ user: usr, children }) => {
const [user, setUser] = useState(usr);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
setAccessToken(user.getIdToken(true));
store.dispatch(userActions.setUser(user));
} else {
setUser(null);
setAccessToken(null);
store.dispatch(userActions.setUser(null));
}
});
return () => {
unsubscribe();
};
}, []);
async function logout() {
return signOut(auth);
}
const value = {
user,
accessToken,
logout,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
return useContext(AuthContext);
};

Firebase createUserProfileDocument() is returning undefined

the createUserProfileDocument() method from firebase is returning undefined for some reason, therefore, it's throwing an error on my broswer's console.
The error thrown is:
App.js:23 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onSnapshot') at App.js:23:1
the code:
import React from 'react';
import './App.css';
import { connect } from 'react-redux';
import { Switch, Route } from 'react-router-dom';
import HomePage from './pages/homepage/homepage.component';
import ShopPage from './pages/shop/shop.component';
import Header from './components/header/header.componet';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';
import SignInAndSignUpPage from './pages/sign-in-and-sign-up/sign-in-and-sign-up.component';
import { setCurrentUser } from './redux/user/user.actions';
class App extends React.Component {
unsubscribeFromAuth = null;
componentDidMount() {
const { setCurrentUser } = this.props;
this.unsubscribeFromAuth = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
console.log(userRef);
userRef.onSnapshot((snapshot) => {
setCurrentUser({
id: snapshot.id,
...snapshot.data(),
});
});
}
setCurrentUser(userAuth);
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return (
<div className='App'>
<Header />
<Switch>
<Route exact path='/' component={HomePage} />
<Route exact path='/shop' component={ShopPage} />
<Route
exact
path='/signin'
component={SignInAndSignUpPage}
/>
</Switch>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: (user) => dispatch(setCurrentUser(user)),
});
export default connect(null, mapDispatchToProps)(App);
The portion of code related to my error is the following:
componentDidMount() {
const { setCurrentUser } = this.props;
this.unsubscribeFromAuth = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
console.log(userRef);
userRef.onSnapshot((snapshot) => { // This is Line 23......
setCurrentUser({
id: snapshot.id,
...snapshot.data(),
});
});
}
setCurrentUser(userAuth);
});
}
I console.logged(userRef) as you can see, and it returns undefined. This is my first experience with firebase so I am not sure where the error is.
I googled and found an answer here in stackoverflow which is alike to my issue: TypeError: Cannot read properties of undefined (reading 'onSnapshot')
However, that problem is specifically related to how the developer was passing the method as an array and not a method, which does not help my situation.
EDIT: Adding firebase.utils code.
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';
const config = {
apiKey: 'AIzaSyDxUMY8sUpOMAoxEyHo41ONilzjL2woWHk',
authDomain: 'crown-db-d4416.firebaseapp.com',
projectId: 'crown-db-d4416',
storageBucket: 'crown-db-d4416.appspot.com',
messagingSenderId: '887003208895',
appId: '1:887003208895:web:778f8f55a0ad2c72953a0a',
measurementId: 'G-6TGCRVN7D3',
};
export const createUserProfileDocument = async (userAuth, additionalData) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
console.log(snapShot);
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt,
...additionalData,
});
} catch (error) {
console.log('Error creating user: ', error.message);
}
}
};
firebase.initializeApp(config);
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const googleProvider = new firebase.auth.GoogleAuthProvider();
export const signInWithGoogle = () => {
auth
.signInWithPopup(googleProvider)
.then((res) => {
console.log(res.user);
})
.catch((error) => {
console.log(error.message);
});
};
export default firebase;
Your createUserProfileDocument function doesn't return anything, so that explains why this code then fails:
const userRef = await createUserProfileDocument(userAuth);
console.log(userRef);
userRef.onSnapshot((snapshot) => {
The solution is to return the userRef from createUserProfileDocument as your calling code expects:
export const createUserProfileDocument = async (userAuth, additionalData) => {
...
return userRef;
};

How to use React Native AsyncStorage with Redux?

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;

Categories

Resources