0

New to React and building a geography app. I have an array of continents uniqueRegions = ['Asia', 'America', 'Europe', 'Africa', 'Oceania', 'Polar'] in my props as this.props.uniqueRegions. In my component, I want to set the state of a property visible on each of those continents. I could hard-code it, but I don't want to do that. How can I possibly make that happen?

I tried using the spread operator as such

state = {
  [ ...this.props.uniqueRegions]: {visible: 5}
    };

but that obviously is incorrect syntax.

Also, I've tried initializing the visible property in setState, but it always returns undefined.

loadMore = (region) => {
        console.log(this.state[region])
        if(this.state[region])
        this.setState(prevState => ({
            [region]: {visible: 5}
        }))
    }

Below is how my region data is passed and currently set to State.

filterRegion = (region) =>{
        if(region !==undefined ){
            this.setState({
                [region]: {countries: ['']}
            })
          return Axios
           .get('https://restcountries.eu/rest/v2/region/' + region)
           .then(res => {
             if(res.status === 200 && res !== null){
              this.setState(prevState =>({
                [region]: {...prevState[region],
                    countries: (res && res.data || [])} 
              }))
              } else {
               throw new Error('No country found');
             }
             })
             .catch(error => {
               console.log(error)
               return []
             });
         };
        };

    loadMore = (region) => {
        console.log(this.state[region])
        if(this.state[region])
        this.setState(prevState => ({
            [region]: {visible: 5}
        }))
    }

    render(){
        const handleRegion =(region) =>{
            this.filterRegion(region);
            if(this.state[region] && this.state[region].open){
                this.setState(prevState =>({
                    [region]: {...prevState[region],
                        open: false
                    }
                  }))
            } else {
                this.setState(prevState =>({
                    [region]: {...prevState[region],
                        open: true
                    }
                  }))
            }
        }

I've been able to set properties on a particular region, i.e. [region].open, but cannot seem to make it work with visible.

EDIT/PROBLEM SOLVED

After some back and forth with Junius, I adjusted my code. What fixed it for me was setting a variable that contained all of the properties per array element that I wanted, adding them to the array, and then setting the entire array as state using the spread operator.

        if (!regions) {
            console.log('no regions')
          return;
        }
        if(regions) 
        console.log(regions);
        const regionsState = {};

        regions.forEach((region, index) => {
            if(this.state[region] && this.state[region].countries[0]){
                regionsState[region] = { visible: 5, countries: this.state[region].countries, open: false};
            } else {
                this.filterRegion(region);
                regionsState[region] = { visible: 5, countries: [], open: false};
            }
        });

        // set state here outside the foreach function
         this.setState((prevState => ({
           ...regionsState
         })))
      };

Lastly, I couldn't get the data to initialize properly. At first, I was loading the data on a click, but that's neither ideal nor realistic; running the code in componentDidMount wouldn't fire after the props were loaded. The solution for me was running the function in componentDidUpdate. My state now initializes correctly. Thanks again Junius for the help!

Khari Kisile
  • 79
  • 3
  • 11
  • Can you give an example of what the desired state should look like? – DonovanM May 03 '19 at 16:10
  • Yes, the desired state should look like `state: {Asia: {visible: 5}, countries: [Array] `. The visible property helps to determine how many countries are shown at a time. A load more button will change the visible state and show that many more countries. – Khari Kisile May 03 '19 at 22:30

3 Answers3

0

create a new object and add each region to the object as a property.

setDynamicRegions = regions => {
  if (!regions) {
    return;
  }

  const regionsState = {};

  regions.forEach((item, index) => {
    regionsState[item] = { visible: 0 };
  });

  //   this.setState({
  //     regionsState: regionsState
 //   });
};

const uniqueRegions = [
    "Asia",
    "America",
    "Europe",
    "Africa",
    "Oceania",
    "Polar"
    ];

    const setDynamicRegions = regions => {
    if (!regions) {
        return;
    }

    const regionsState = {};

    regions.forEach((item, index) => {
        regionsState[item] = { visible: 0 };
    });

    //   this.setState({
    //     regionsState: regionsState
    //   });
    console.log(regionsState);
    };

    setDynamicRegions(uniqueRegions);
Junius L
  • 15,881
  • 6
  • 52
  • 96
  • This works well for setting the values to regionState, but when attempting to set state, the app crashes with this 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.` – Khari Kisile May 03 '19 at 22:36
  • how do you set state? – Junius L May 04 '19 at 18:43
  • I'm currently setting state in the place where this.setState is grayed out. – Khari Kisile May 05 '19 at 05:13
  • ````regions.forEach((item, index) => { regionsState[item] = { visible: 0 }; this.setState(prevState => ({ [item]: {...prevState[item], visible: 0, countries: []} })); console.log(this.state[item]) }); };```` – Khari Kisile May 05 '19 at 05:15
  • No you are setting state inside `.forEach()` it won't work, it'll throw that error, move set state below foreach. – Junius L May 05 '19 at 06:15
  • No, I set and ran the function inside the render function, and it returned the same error: maximum update depth exceeded – Khari Kisile May 05 '19 at 09:58
  • it shouldn't be in a render function. let me see your code paste it here https://codeshare.io/2jJOnM – Junius L May 05 '19 at 10:03
  • https://github.com/capstonednc/geography-app My laptop is dying, and I'm currently unable to charge it. Perhaps you can fork the app, or I can try stackblitz again when I'm back online. I truly appreciate your assistance thus far! – Khari Kisile May 05 '19 at 10:47
  • Thanks for the assistance! – Khari Kisile May 06 '19 at 17:10
0

You can use a simple empty object in state:

state = {
    visibleRegions: {}
}

Making region visible is as simple as define property with region as key

visibleRegions[region] = true;

Condition !visibleRegions[region] will cover undefined and false ... !!visibleRegions[region] for visible state.

xadm
  • 8,219
  • 3
  • 14
  • 25
0

I did it with a checkbox, you can adapt it as you wish to any element, it is the same principle, I don't know if this is what you look for because I am not sure if I got your question, but I hope at least it gives you an idea. Take your array from your props const {myData} = this.props Good luck :)

`

class Geo extends Component { state = { visible: false, uniqueRegions: ['Asia', 'America', 'Europe', 'Africa', 'Oceania', 'Polar'] };

handleChecked = () => {
    this.setState({
        visible: !this.state.visible
    });
};
render() {
    const { uniqueRegions } = this.state;
    return (
        <div>
            <input type="checkbox" onChange={this.handleChecked} value={uniqueRegions} />

            {uniqueRegions.map(region => {
                if (this.state.visible) {
                    return (
                        <p>
                            these are my regions {region}
                        </p>
                    );
                } else {
                    return null;
                }
            })}
        </div>
    );
}

}

export default Geo; `