DEV Community

Cover image for Expo with Redux Toolkit, File System, and Redux Persist: A Comprehensive Guide
VARNIT JAIN
VARNIT JAIN

Posted on

Expo with Redux Toolkit, File System, and Redux Persist: A Comprehensive Guide

Redux Toolkit is a popular library that simplifies Redux development by providing a set of utilities and conventions. It includes a reducer and action creation patterns that streamline the process of writing Redux logic. Combining Redux Persist with Redux Toolkit can significantly enhance the efficiency and reliability of state management in your React Native apps.

expo-file-system provides access to a file system stored locally on the device. It is also capable of uploading and downloading files from network URLs

redux-persist-expo-file-system-storage is a storage engine for redux-persist that uses Expo's File System API. This allows you to persist your Redux store's state to the device's file system, ensuring that the state is saved and restored even if the app is closed or restarted.

Once you have configured your Redux Toolkit store, you can integrate Redux Persist. Begin by installing the necessary dependencies:

npm i @reduxjs/toolkit 
npm i expo-file-system 
npm i redux-persist-expo-file-system-storage
npm i redux-persist
Enter fullscreen mode Exit fullscreen mode

we create a simple Redux store @reduxjs/toolkit to manage a counter state. We define an initial state with a count of 0 and create a slice named counter with two reducers: increment and decrement, which increase and decrease the count by 1, respectively. The actions and the reducer are exported for use in our Redux setup, allowing us to easily manage the counter state throughout the application.

// store/counter/counter.tsx
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  count: 0,
};

const counterSlice = createSlice({
  name: "couter",
  initialState,
  reducers: {
    increment: (state) => {
      state.count += 1;
    },
    decrement: (state) => {
      state.count -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

The code sets up a Redux store in a React Native app using @reduxjs/toolkit and redux-persist with Expo's FileSystem for persistent storage. It combines reducers, including a counter reducer, and ensures their state is saved and loaded from a specified directory within the app's file system.

// store/ConfigureStore.js
import { combineReducers } from "@reduxjs/toolkit";
import { documentDirectory, EncodingType } from "expo-file-system";
import { createExpoFileSystemStorage } from "redux-persist-expo-file-system-storage";

import CounterReducer from "./counter/counter";
import { persistReducer } from "redux-persist";
console.log('Document Directory:',documentDirectory);
export const expoFileSystemStorage = createExpoFileSystemStorage({
  storagePath: `${documentDirectory}customPathName/`,
  encoding: EncodingType.UTF8,
  debug: true,
});
const persist = (key, reducer) =>
  persistReducer(
    {
      key,
      storage: expoFileSystemStorage,
    },
    reducer
  );

const combinePersistReducers = (keys) =>
  Object.keys(keys).reduce(
    (obj, key) => ({
      ...obj,
      [key]: persist(key, keys[key]),
    }),
    {}
  );

const reducers = combineReducers({
  ...combinePersistReducers({
    count: CounterReducer,
  }),
});

export default reducers;

Enter fullscreen mode Exit fullscreen mode

In this part of the setup, we configure and create the Redux store using @reduxjs/toolkit. We import the rootReducer that combines our reducers and disable the serializableCheck middleware to prevent serialization errors from redux-persist. We also set up redux-persist to enable state persistence and export both the store and the persistor for integration with our React Native application. This configuration ensures our app's state is maintained even after it is closed or refreshed.

// store/index.js
import { configureStore } from "@reduxjs/toolkit";
import { persistStore } from "redux-persist";
import { Provider } from "react-redux";
import rootReducer from "./ConfigureStore";

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});
const persistor = persistStore(store);

export { store, persistor };

Enter fullscreen mode Exit fullscreen mode

By logging and understanding the documentDirectory path in the configureStore file, you gain valuable insight into where your Redux Persist data is stored in an Expo environment. This knowledge not only helps in debugging but also ensures that your data persistence strategy is robust and secure. Now you can confidently manage your app's state, knowing exactly where your data lives.

Within this directory, redux-persist will store your persisted state. Typically, the persisted data is stored in a file named persist-counter (or something similar, depending on your configuration). Here’s how you can navigate to and view this file:

// persist-count file

{"count":"0","_persist":"{\"version\":-1,\"rehydrated\":true}"}

Enter fullscreen mode Exit fullscreen mode

For a complete example, you can check out the GitHub repository for this project: GitHub Repository Link

💬 I’d love to hear your thoughts on this topic! If you have any questions about using Expo File System with Redux Persist, or if you run into any issues, please drop a comment below. I’m here to help! 😄

Top comments (4)

Collapse
 
hatta_food_6b1eee94b41c7e profile image
Hatta Food

is this similar to async storage ?

Collapse
 
varnitj178 profile image
VARNIT JAIN

Yes, it's the same as that both expo File System storage and Async Storage can be used to persist redux state across apps.
File system might be advantageous if your app have to handle larger or more complex data

Collapse
 
hatta_food_6b1eee94b41c7e profile image
Hatta Food

i have now used react-native-mmkv which is swift n doesn't need async call does it work similarly to that as well

Thread Thread
 
varnitj178 profile image
VARNIT JAIN

Yes, most probably it will be working similarly to this just we have to configure it properly to achieve this functionality