Rohan Naik
Rohan Naik

Reputation: 105

componentDidUpdate() going in an infinite Loop

I am new to react and react native and trying to understand the lifecycle of React components... I have four switches on my screen. I am trying to save the current state of the switches as an object (applied Filters) and then pass it to the params of the navigation props and access it in the onPress function of the save button in the navigation header...

import React, { useState, useEffect, useCallback } from 'react';
import { View, Text, StyleSheet, Switch } from 'react-native';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import HeaderButton from '../components/HeaderButton';

const FilterSwitch = (props) => {
  return (
    <View style={styles.filterContainer}>
      <Text>{props.label}</Text>
      <Switch
        value={props.state}
        trackColor={{ true: 'yellow' }}
        thumbColor={{ true: 'blue' }}
        onValueChange={props.onChange}
      />
    </View>
  );
};

class FilterScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGlutenFree: false,
      isLactoseFree: false,
      isVegan: false,
      isVegetarian: false,
    };
    this.setisGlutenFree = this.setisGlutenFree.bind(this);
    this.setisLactoseFree = this.setisLactoseFree.bind(this);
    this.setisVegan = this.setisVegan.bind(this);
    this.setIsVegetarian = this.setIsVegetarian.bind(this);
    this.saveFilters = this.saveFilters.bind(this);
  }
  setisGlutenFree(newValue) {
    this.setState({
      isGlutenFree: newValue,
    });
  }
  setisLactoseFree(newValue) {
    this.setState({
      isLactoseFree: newValue,
    });
  }
  setisVegan(newValue) {
    this.setState({
      isVegan: newValue,
    });
  }
  setIsVegetarian(newValue) {
    this.setState({
      isVegetarian: newValue,
    });
  }
  saveFilters() {
    const appliedFilters = {
      glutenFree: this.state.isGlutenFree,
      lactoseFree: this.state.isLactoseFree,
      vegan: this.state.isVegan,
      Vegetarian: this.state.isVegetarian,
    };
    console.log(appliedFilters);
  }

  componentDidMount() {
    this.props.navigation.setParams({ save: this.saveFilters });
  }
  componentDidUpdate() {
    this.props.navigation.setParams({ save: this.saveFilters });
  }

  render() {
    return (
      <View style={styles.screen}>
        <Text style={styles.title}>Available Filters / Restrictions</Text>
        <FilterSwitch
          label="Gluten-free"
          state={this.state.isGlutenFree}
          onChange={(newValue) => this.setisGlutenFree(newValue)}
        />
        <FilterSwitch
          label="Lactose-free"
          state={this.state.isLactoseFree}
          onChange={(newValue) => this.setisLactoseFree(newValue)}
        />
        <FilterSwitch
          label="Vegan"
          state={this.state.isVegan}
          onChange={(newValue) => this.setisVegan(newValue)}
        />
        <FilterSwitch
          label="Vegetarian"
          state={this.state.isVegetarian}
          onChange={(newValue) => this.setIsVegetarian(newValue)}
        />
      </View>
    );
  }
}

FilterScreen.navigationOptions = (navData) => {
  return {
    headerTitle: 'Filter Meals',
    headerLeft: () => (
      <HeaderButtons HeaderButtonComponent={HeaderButton}>
        <Item
          title="Menu"
          iconName="ios-menu"
          onPress={() => {
            navData.navigation.toggleDrawer();
          }}
        />
      </HeaderButtons>
    ),
    headerRight: () => (
      <HeaderButtons HeaderButtonComponent={HeaderButton}>
        <Item
          title="Save"
          iconName="ios-save"
          onPress={() => {
            console.log('Item Saved');
            console.log(navData.navigation.getParam('save')());
          }}
        />
      </HeaderButtons>
    ),
  };
};

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    alignItems: 'center',
  },
  title: {
    fontFamily: 'open-sans-bold',
    fontSize: 22,
    margin: 20,
    textAlign: 'center',
  },
  filterContainer: {
    marginVertical: 10,
    borderWidth: 1,
    borderRadius: 10,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '80%',
    height: '10%',
    paddingLeft: 10,
  },
});

export default FilterScreen;

the problem is that as soon as we enter the filter to screen the lifecycle methods are going in an infinite loop.... and the following error pops out following a huge stack trace.

    Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState 
    inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Guys I want to access the state parameters outside the component in the navigation header everytime the state gets updated.... so I thought everytime component gets updated the navigation params will get updated state parameters

Upvotes: 0

Views: 1309

Answers (2)

nazmul
nazmul

Reputation: 435

Only update something if they have changed in componentDidUpdate to avoid infinite loop,

componentDidUpdate(prevProps, prevState) {
  if (prevProps.something !== this.props.something) {
    console.log('something  has changed.')
  }
}

Upvotes: 1

D10S
D10S

Reputation: 1549

componentDidUpdate by definition is being called every time component is being updated, i.e. when state or props change.

If you will update the state or you props in this function you will cause an infinite loop...

Upvotes: 1

Related Questions