oneWorkingHeadphone oneWorkingHeadphone - 3 years ago 49
React JSX Question

Component not returning UI even though props are being delivered

I'm fairly new to React so I think the problem here is likely to be pretty basic. I'm displaying a list of regions and when a user clicks on a region I am passing an object via props to a CountryList component:

render() {
return (
<div>
<SelectRegion
selectedRegion={this.state.selectedRegion}
onSelect={this.updateBoth} />
{ !this.state.countryCodes
? "Select a region"
: <CountryList countryCodes={this.state.countryCodes} /> }
</div>
)
}


The SelectRegion component works fine, but the CountryList is only rendering the component without content. Here is the CountryList component.

function CountryList(props) {
return (
<ul className="country-list">
{Object.entries(props.countryCodes).forEach(([key, val]) => {
return (
<li
key={key}
className="country-item" >
<div className="country-code">{key}</div>
<ul className="space-list-items">
<li>
<img
className="flag"
src={`http://www.geognos.com/api/en/countries/flag/${key}.png`}
alt={"Flag for " + val}
/>
</li>
</ul>
</li>
)
})}
</ul>
)
}


If I look in the inspector, I can see that when the user clicks on the region the state is updated, the CountryList component is added to the page (as well as the
<ul></ul>
) and that it has the appropriate props (an object). What am I missing that's keeping the component from displaying?

If it's helpful, this is the entire component:



function App () {
return (
<div>
<Selector />
</div>
)
}

function SelectRegion (props) {
const regionCountry = {
"Europe": {
"PL": "Poland",
"HU": "Hungary",
"DE": "Germany",
"AT": "Austria",
"DK": "Denmark",
"ES": "Spain",
"GR": "Greece",
"IT": "Italy",
"CH": "Switzerland",
"RU": "Russia",
"FR": "France",
"BE": "Belgium",
"LU": "Luxembourg",
"SE": "Sweden",
"NO": "Norway",
"SI": "Slovenia",
"LT": "Lithuania",
"CY": "Cyprus",
"LV": "Latvia",
"BG": "Bulgaria",
"HR": "Croatia",
"GB": "United Kingdom",
"IE": "Ireland",
"GE": "Georgia",
"RO": "Romania",
"FI": "Finland",
"NL": "Netherlands",
"ME": "Montenegro"

},
"Americas": {
"CA": "Canada",
"US": "USA",
"MX": "Mexico",
"BR": "Brazil",
"CL": "Chile",
"AR": "Argentina",
"CO": "Columbia",
"UY": "Uruguay"
},
"APAC": {
"AU": "Australia",
"NZ": "New Zealand",
"KZ": "Kazakhstan",
"JP": "Japan",
"TH": "Thailand",
"TW": "Taiwan"
},
"Middle East & Africa": {
"IL": "Israel",
"TR": "Turkey",
"AE": "UAE",
"SA": "South Africa"
}
}

return (
<ul className="regions">
{Object.keys(regionCountry).map((region) => {
return(
<li
style={region === props.selectedRegion ? {color: '#d0021b'} : null}
onClick={props.onSelect.bind(null, region, regionCountry[region])}
key={region} >
{region}
</li>
)
})}
</ul>
)
}

function CountryList(props) {
return (
<ul className="country-list">
{Object.entries(props.countryCodes).forEach(([key, val]) => {
return (
<li
key={key}
className="country-item" >
<div className="country-code">{key}</div>
<ul className="space-list-items">
<li>
<img
className="flag"
src={"http://www.geognos.com/api/en/countries/flag/"+key+".png"}
alt={"Flag for " + val}
/>
</li>
</ul>
</li>
)
})}
</ul>
)
}

class Selector extends React.Component {
constructor(props) {
super(props);

this.state = {
selectedRegion: null,
countryCodes: null
}

this.updateRegion = this.updateRegion.bind(this);
this.updateCountries = this.updateCountries.bind(this);
this.updateBoth = this.updateBoth.bind(this);
}

updateRegion(region) { selectedRegion: region }

updateCountries(countries) { countryCodes: countries }

updateBoth(updateRegion, updateCountries) {
this.setState(() => {
return {
selectedRegion: updateRegion,
countryCodes: updateCountries
}
})
}

render() {
return (
<div>
<SelectRegion
selectedRegion={this.state.selectedRegion}
onSelect={this.updateBoth} />
{ !this.state.countryCodes
? "Select a region"
: <CountryList countryCodes={this.state.countryCodes} /> }
</div>
)
}
}

ReactDOM.render(
<App />,
document.getElementById('app')
)

body {
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;
}

.container {
max-width: 1200px;
margin: 0 auto;
}

a {
text-decoration: none;
color: #d0021b;
}

ul {
padding: 0;
}

li {
list-style-type: none;
}

.button {
color: #e6e6e6;
background: #0a0a0a;
border: none;
font-size: 16px;
border-radius: 3px;
width: 200px;
text-align: center;
display: block;
padding: 7px 0;
margin: 10px auto;
}

.button:hover:enabled {
background: linear-gradient(#1a1a1a,#0a0a0a);
color: #fff;
text-decoration: none;
}

.button:active {
transform: translateY(1px);
}

.regions {
display: flex;
justify-content: center;
}

.regions li {
font-size: 1.5em;
margin: 10px;
font-weight: bold;
cursor: pointer;
}

.country-list {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}

.country-item {
margin: 20px;
text-align: center;
}

.country-code {
margin: 20px;
text-align: center;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Express</title>
</head>
<body>
<div id="app"></div>
</body>
</html>




Answer Source

The empty <ul></ul> must mean that the props.countryCodes is not set, the loop for Object.entries looks good to me. The problem looks like your callback 'updateBoth' is to blame, onSelect={this.updateBoth} passes no parameters to updateBoth(updateRegion, updateCountries) so your state cannot set any value for countryCodes.

Edit

I can make your code work with:

function CountryList(props) {
    console.log(props);
    return (
        <ul className="country-list">
            {
                Object.keys(props.countryCodes).map((key) => {
                    console.log(key);
                    return (
                        <li
                            key={ key }
                            className="country-item" >
                            <div className="country-code">{key}</div>
                            <ul className="space-list-items">
                                <li>
                                    <img
                                        className="flag"
                                        src={"http://www.geognos.com/api/en/countries/flag/"+key+".png"}
                                        alt={"Flag for " + props.countryCodes[key]}
                                    />
                                </li>
                            </ul>
                        </li>
                    );
                })

            }
        </ul>
    )
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download