Recursive Loop in Redux Reducer to check integrity of local Storage
Tags: reactreduxrecursive
Published: 2018-05-12 19:37:18

---
title: Recursive Loop in Redux Reducer to check integrity of local Storage
published: true
description: Recursive Loop to check keys inside of a reducer state and replace them if they are not found.
tags: react redux recursive
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2bc7a18u3y4yxfuyljnv.jpg
---


## The Problem:
> When we would add nested Settings inside of our local storage object, users were getting errors that were pointing to our MapStateToProps, the keys in their local Storage were missing our new updates.

## the Error: 

![WHy?!?!?](https://thepracticaldev.s3.amazonaws.com/i/zirznpyl3dvc9dqxp22i.png "BugginOut")



### Our Local Storage Object: 

we used local storage to save things like tab state , and in this instance we saved what was the last cover page / header you created in your last document.


_reducers/local-storage-settings.js_

```javascript

const DEFAULT_SETTINGS = {
  docs: {
    settings: {
      coverPageId: null,
      headerId: null
    }
  },
  projectInfoOpen: true,
  lastSelectedInfoTab: null,
  lastSelectedAboutTab: null,
  lastSelectedContactTab: null,
  lastSelectedRegisterTab: null,

};

```

When a user logs in to our application we check their local storage and set Our redux store equal to the object we saved into their local storage during their last visit. 

The issue is when we add new fields to this object, we want to have nested fields so that it looks pretty right? 

But if we have nested fields , we want to be able to check to make sure those keys exist 

### When?!

Well since this is dealing with the state , we do not want this function running all the time, the best place I could think of was putting it on the `componentWillMount` inside of our base container inside of the project. (we had a local storage object for each project) 

```javascript

  componentWillMount() { 
    // // NOTE: ensures the sanity of our local storage with recursive method
    this.props.ensureAllFieldsPresent({
      projectUuid: this.props.params.projectUuid
    });
  }

```

So when the action was called we would run this function, only passing the `projectUuid` to identify which project in local storage we wanted to check .


### Whats happening in the reducer?


_reducers/local-storage-settings.js_

```javascript 

function ensureAllFieldsPresent(state, { projectUuid }) {
  let newState = JSON.parse(JSON.stringify(state));
  let fixedProjectState = recursiveCheck(newState[projectUuid]);
  newState[projectUuid] = fixedProjectState;
  localStorage.setItem('projectSettings', JSON.stringify(newState));
  return newState;
}


```

So for each project we will run our recursive check, set local storage settings to the new 'fixed' state , and than lastly return the newState to our redux Store.


```javascript
function recursiveCheck(state, defaultSettings = DEFAULT_SETTINGS) {
  let newState = JSON.parse(JSON.stringify(state));

  for (let k in defaultSettings) {
    if (!newState[k]) {
      // NOTE: 'in the event that a value is default set to TRUE'
      if (newState[k] !== false) {
        newState[k] = defaultSettings[k];
      }
    } else if (typeof defaultSettings[k] == 'object') {
      recursiveCheck(newState[k], defaultSettings[k]);
    }
  }
  return newState;
}

```
### Whats GOING ON ??!

```javascript

function recursiveCheck(state, defaultSettings = DEFAULT_SETTINGS) {...}

```

because we will be calling this function again (from within the function), we set a defaultSettings default so that when the method is called initially the first state object we use is the `DEFAULT_SETTINGS` object , which has the updated values we will be using to correct the users corrupt local storage object.


```javascript 

let newState = JSON.parse(JSON.stringify(state));

```

since redux's state object is `readonly` you cannot mutate the object and return it , your must make a copy , make modifications and than return a new state.

were using `JSON.parse(JSON.stringify(state)` to make a deep copy of the object and get all those fields. (IF YOU KNOW A FASTER, BETTER WAY , COMMENT BELOW !)


```javascript

  for (let k in defaultSettings) {
    if (!newState[k]) {
      // NOTE: 'in the event that a value is default set to TRUE'
      if (newState[k] !== false) {
        newState[k] = defaultSettings[k];
      }
    }

}
```

the `for` loop : this will looop through our `DEFAULT_SETTINGS` Object and check (compare our default object to the users state) to see if we made any updates to any fields , but notice that it will only check the top level of the object, we want to go deeper and check the rest of the fields that are nested inside.

```javascript
if (!newState[k]){...}
```

So if the users local storage setting does not have a value in their local state , we want to create one for them (this covers `null` and `undefined`)

__this part took me like a day to figure out__

```javascript

if (newState[k] !== false) {
        newState[k] = defaultSettings[k];
}

```
so if the users state has a boolean `false` in their store and they are on the view in which the main component is mounted and this method is run, which is definitely possible, than what will happen is that this method will run and reset their store to `DEFAULT_SETTINGS` even though they they just clicked to set whatever option to false. 

in this instance, when a user clicked to close something, it was not remembering that they closed it , and when we would navigate away and come back it was open again. 

so in addition to checking `!newState[k]` i wrote another conditional inside (i know i could have done it in 1 line) to check to make sure that the `newState[k]` is initially not false. and if its not initially false we will replace the object.

### The recursive.ness 

So what happens when we run into an object in this loop? 

We want to go deeper in to this object, and run the same key checking.

```javascript

else if (typeof defaultSettings[k] == 'object') {
      recursiveCheck(newState[k], defaultSettings[k]);
    }

```

So now when the loop gets to a value that is an object, this means we can go deeper , and check the keys inside of this object. 

remember at the beginning, the initial state we passed (as the second argument) was `DEFAULT_SETTINGS`

```javascript 
function recursiveCheck(state, defaultSettings = DEFAULT_SETTINGS) {...}

```

So this time, we will pass in the `default[k]` part of the state into the method as the second argument , and `newState[k]` as the first argument, this will ensure that the correct part of the users state object, and the correct part of the `DEFAULT_SETTINGS` is checked against each other. 

### NICE

now at the end of this we have fully checked every corner of this users Local storage settings, and now hopefully wont run into any bugs when we add new settings to the app.

# Thanks so much for reading!