Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How and where to save the whole redux store using AsyncStorage

I was wondering if I have to do something like this to save the whole store of my app using AsyncStorage in my react-native app and check if it exists when the app is starting.

var store;

_loadInitialState = async () => {
  try {
    var value = await AsyncStorage.getItem(STORAGE_KEY);
    if (value !== null){
      store = value;
      console.log('Recovered selection from disk: ' + value);
    } else {
      store = configureStore({});
      console.log('Initialized with no selection on disk.');
    }
  } catch (error) {
    console.log('AsyncStorage error: ' + error.message);
  }
};



export default class App extends Component {

  componentWillMount(){
    this._loadInitialState().done()
  }

  render(){
    return(
      <Provider store={store}>
        <AppContainer/>
      </Provider>
    )
  }
}

This is not working but I hope you get the idea.

like image 665
Luis Rizo Avatar asked Feb 27 '17 21:02

Luis Rizo


People also ask

Where is the redux store saved?

The state in Redux is stored in memory. This means that, if you refresh the page the state gets wiped out. The state in redux is just a variable that persists in memory because it is referenced by all redux functions.

Where does AsyncStorage store data?

On iOS, AsyncStorage is backed by native code that stores small values in a serialized dictionary and larger values in separate files. On Android, AsyncStorage will use either RocksDB or SQLite based on what is available.

How do I save to redux store?

To set up the redux store, import the configureStore, and setupListeners. Import the apiSlice and useCartReducer from their respective files. Next, import the combineReducers from the redux toolkit library. The combine reducer groups any number of reducers in a single object and returns a reducer object.

How do you save AsyncStorage data?

Using the setItem() method The setItem method saves data to the AsyncStorage and allows a key and a value. Here, the key is a string that the data or value is assigned to: // React Native component const value = { name: "Chimezie", job: "Software Developer" }; const storeUser = async () => { try { await AsyncStorage.


2 Answers

Yes, this is one of the ways your app can retrieve its state from AsyncStorage. However, the way you want to handle data depends on the app and on the data itself.

Personally I tend not to save the whole app store. Some sections of your app might need to be re-fetched when navigate between scenes, it doesn't make sense to keep that in AsyncStorage. Same goes about the current scene and similar data, that is to be kept in redux store, but will probably be reset on app restart.

Regarding saving the data to AS, the way I'd recommend is redux middleware. You intercept the actions you need, and do AsyncStorage.setItem(). I guess it's ok to do that elsewhere, like in the components code. It's not that clean, but it's up to your taste, really. Note regarding the keys - I just store different datasets in different AsyncStorage keys. This way you handle less data on every store update (not the whole state tree, but it's section only). The middleware code will look similar to the following:

export default store => next => (action) => {
  let result = next(action)

  switch (action.type) {
    case THE_ACTION_THAT_REQUIRES_AN_ASYNCSTORAGE_UPDATE:
      AsyncStorage.setItem('@your_key', action.data_that_is_to_be_stored)
      break

    case ANOTHER_ACTION:
      // similar here
      break

    default:
      break
  }

  return result
}

Note that you don't need to do await here since in most cases it's ok to write data asynchronously (unless you are going to read and use it right away, which shouldn't be the case if you are using redux store)

Regarding app start - you can use AsyncStorage.multiGet to get all keys right away and put them in store. Following the RN docs:

AsyncStorage.getAllKeys((err, keys) => {
  AsyncStorage.multiGet(keys, (err, stores) => {
   stores.map((result, i, store) => {
     let key = store[i][0]
     let value = store[i][1]

     // add your data to redux store
    })
  })
})

Once you read the AsyncStorage and get the data, it makes sense to combine it into a preloaded state object that will be similar to your app store and set the Redux store via a single action. You can call it APP_STATE_INIT, pass the newly combined object to it and catch the action in your reducers which need to use the data. This way you'll just have a single action to rehydrate your state on app start.

You'll probably need some loading state that will affect the loader being displayed until the AS values get fetched and the data gets populated into redux store. But overall you are right, you can put the AsyncStorage init and wait time to componentWillMount

...
async componentWillMount() {
  await _loadInitialState()

  this.setState({
    isLoading: false
  })
}
...

If you think that for your app it's ok to save everything and boot up based on that, feel free to use one of the packages that give a simple way to rehydrate app store from storage. I'd just note potential effect on performance and the fact that this way you lose control over what's going on.

like image 82
dhorelik Avatar answered Oct 14 '22 20:10

dhorelik


Use redux-persist so you don't have to use AsyncStorage directly.

like image 28
wmcbain Avatar answered Oct 14 '22 19:10

wmcbain