user10931821
user10931821

Reputation:

Why am I overwriting all of my values in state with my object spread?

In my handleChange function I use an object spread to get all prior key/vals from this.state and I add on a new value to the selectedCountry key. My issue is that I keep reassigning the countryFlags value which messes up my app's state. Am I missing something? I've really wrestled with this for hours and I cannot figure out the reason for this. Is it my overall implementation of setState. Thank you in advance for your observation.

import React, {Component} from "react"
import FlagSelect from "./FlagSelect";


export default class FlagQuiz extends Component   {  
    constructor()  {
        super()
        this.state = {
            countryFlags: undefined,
            selectedCountry: ""
        }
        this.handleChange = this.handleChange.bind(this)
    }

    componentDidMount(){
        fetch("https://restcountries.eu/rest/v2/all")
            .then(function(response, reject) { 
                return response.json()
            })
            .catch(() => console.log("nope"))
            .then((data) => {
                this.setState({countryFlags: data})
            });

    }

    handleChange(event){
        event.preventDefault();
        const {name, value, type, checked} = event.target
        // console.log(this.state)
        const newState = {...this.state}
        newState.selectedCountry = value
        // console.log(newState)

        ///STOP UPDATING THE countryFlgs!!
        this.setState({...this.state, selectedCountry: value})
        console.log(this.state)

    }

    render(){
        const flagStyle = {
            display: "block",
            marginLeft: "auto",
            marginRight: "auto",
            width: "50%"
        }
        const src = this.state.countryFlags === undefined ? "" : this.state.countryFlags[0].flag
        const buttonStye = {
            background:"lightblue",
            color: "white",
            fontSize: "30px",
            float: "right"
        }
    const countries = this.state.countryFlags ? [
            Math.floor(Math.random() * this.state.countryFlags.length),
            Math.floor(Math.random() * this.state.countryFlags.length),
            Math.floor(Math.random() * this.state.countryFlags.length),
            Math.floor(Math.random() * this.state.countryFlags.length)
        ] : ""

        const fourCountries = () => {
            let flagselects = countries ? countries.map((countrynum) =>{
                return <FlagSelect 
                handleChange={this.handleChange} 
                checked={this.state.selectedCountry}
                key={countrynum}
                countryinfo={this.state.countryFlags[countrynum].name}/>
            }) : null

            return flagselects
        }

        return (
            <div>
            <form>
                <div className="form-check">
                {fourCountries()}
                <div className="form-group">
                <button style={buttonStye} >Guess</button>
                </div>
                </div>
            </form>

                <h1 style={{textAlign:"center"}}>Flag Quiz</h1>
                <img src={src} alt="flag" style={flagStyle}
                />
                {this.state.countryFlags ? console.log(this.state.countryFlags.length): ""}
            </div>

        )
    }

}

My results are a selectedCountry but with a whole new set of randomized selection of countries from the countryFlags array.

Upvotes: 0

Views: 47

Answers (1)

Sulthan
Sulthan

Reputation: 130112

Changing state is simple:

this.setState({ selectedCountry: value })

Nothing else is needed.

However, changing state will always cause a rerender. And I see a big problem with your rendering.

Everytime render is called, you will randomize your flags:

    const countries = this.state.countryFlags ? [
        Math.floor(Math.random() * this.state.countryFlags.length),
        Math.floor(Math.random() * this.state.countryFlags.length),
        Math.floor(Math.random() * this.state.countryFlags.length),
        Math.floor(Math.random() * this.state.countryFlags.length)
    ] : ""

Therefore your UI will change.

Render is supposed to always return the same thing if properties and state have not changed. However, your render will always display different things depending on the random numbers.

You have to move your randomization to your state, for example:

.then((data) => {
    const countries = [
         Math.floor(Math.random() * data.length),
         Math.floor(Math.random() * data.length),
         Math.floor(Math.random() * data.length),
         Math.floor(Math.random() * data.length)
    ]
    this.setState({ countryFlags: data, countries })
});

Upvotes: 1

Related Questions