Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native - Redux : Multiple instances of same state in app

I am a newbie in React Native. I am using redux architecture for my react native app however I am facing a problem due to global store state of redux.

Suppose for example, While going forward in the app I am navigating like below. enter image description here

While back navigating,

enter image description here

According to redux architecture, It is changing the state of every instance of the page present in the navigation stack with the latest state in the store.

Here is the code for the above example,

Page.js [Component]

class Page extends Component{

    componentWillMount(){

    }

    render(){
        var {image,apiCall} = this.props;

        return (
        <View>
            <Image Source={{uri:image}} style={{// style for the image}}>
        </View> 
    )}

    componentDidMount(){
        this.props.apiCall(this.props.route.data.link);   // this.props.route.data.link -> this is just a link for                                                  // the apiCall
    }

    componentWillReceiveProps(){

    }

    shouldComponentUpdate(nextProps, nextState){
        if(this.props.image==nextProps.image){
          return false;
        }
        return true;
    }

    componentWillUpdate(){

    }

    componentDidUpdate(){

    }

    componentWillUnmount(){

    }
}

   const mapStateToProps = (state) => {
     return {
     image: state.PageDataReducer.image
     };
   };

   const mapDispatchToProps = (dispatch) => {
     return {
       apiCall: (link) => {
         dispatch(fetchProduct(link));  // fetchProduct() -> is a function which fetches the product from the                                  // network and dispatches the action.
       },
     };
   };

export default connect(mapStateToProps,mapDispatchToProps)(Page);

fetchProduct() [A function to fetch product]

export function fetchProduct(link) {
  return function(dispatch){
    // fetches the product over network
    dispatch(fetchedProduct(productLink));
  }
}

fetchedProduct [Action]

export const fetchedProduct = (productLink) => {  
  return {
    type: FETCHED_PRODUCT,
    image: image
  };
};

PageDataReducer [Reducer]

export default function PageDataReducer(state = initialState, action) {

   switch (action.type) {

    case FETCHED_PRODUCT:
    var newState = {
      ...state,
      image: action.image
    };
      return newState;

    default:
      return state;
    }
}

My observation : Whenever same page occurs in the navigation and the state in the store of that page gets change then it calls mapStateToProps() of that page the number of times that the page is in the navigation stack. And hence that number of times it loops through the lifecycle methods of that page to change the state according to latest state.
In this example, when I click on the banana in the drawer, it changes the state from mango to banana in the store and mapStateToProps() gets called 3 times (because that page is present 3 times in the navigation stack) and hence all the lifecycle methods of react from componentWillReceiveProps() to componentDidUpdate() gets called 3 times just to change the state of that page according to the latest state.

What I want : I want the different state of the page in the navigation, So that while going back I can see all the different products I have visited.

The problem is obvious according to redux architecture but I am not getting the trick to solve it. Any help or any other work around to achieve my goal would be greatly appreciated.

like image 940
Kalpesh Wadekar Avatar asked Oct 13 '16 13:10

Kalpesh Wadekar


1 Answers

You should decouple storage of your domain data, such as data about apple, mango, and banana, from storage of your UI state, such as which item is currently selected. In the domain data part you would store all the items that have once been loaded (you may want to later add some garbage collection policy such as least recently used lists to save some memory).

The view components would then connect to both parts of the app state tree: to the UI part to get what is currently selected, and to the domain part to get the details about the selected item.

In your mapStateToProps function first extract the current selection, this has to be an identifier. You may also extract it from somewhere else, e.g. from the component props (the second argument to mapStateToProps). Then you use that extracted identifier to get the already loaded data from the domain data store. If the data that is selected is not in the store (e.g. when the user visits the page for the first time, or it has already been garbage-collected), you put empty value into props, and this would mean to componentDidMount or componentWillReceiveProps hooks that they have to make a data load request (it may be implemented as a dispatch of an action that signals the data demand to a saga or a thunk, depending on what you use for managing data loading), while this is happening the render would return some "loading..." stub. When the request finishes, another dispatch would update the domain data store update, which would trigger connect to make another mapStateToProps call, and this would result in discovering that the selected data is already in the store, so no new data load request will be made, and the render will have enough data for display.

like image 157
sompylasar Avatar answered Oct 12 '22 23:10

sompylasar