1
votes

UPDATE: Turns out everything was working fine. I just had an error in my className Ternary that was causing the class not to apply. However, could someone explain why onClick does not appear on the <div> in the inspector?

I have a react component in which I would like to render a <div> with an onClickevent. However, the onClickis not rendering for some reason. I have no errors in the console other than the warning: Each child in an array or iterator should have a unique "key" prop.

The <div> that actually renders looks like this: <div id="1" class="unselected">Peppered Chicken</div>

My code looks like this. Thoughts?

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

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

    render() {
        return (<div><RecipesList/></div>)
    }
}

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

        this.state = {
            recipes: [{recipes:[]}],
            selectedRecipe: null
        };

        this.setRecipeAsSelected = this.setRecipeAsSelected.bind(this)
    }

    componentDidMount() {
        fetch('/recipes')
            .then(response => response.json())
            .then(recipes => this.setState({recipes}));
    }

    setRecipeAsSelected(recipeID) {
        this.setState({selectedRecipe: recipeID})
    }

    render() {
        return (
            <div>
                {this.state.recipes[0]["recipes"].map((recipe, index) => {
                    return <Recipe setRecipeAsSelected={this.setRecipeAsSelected} selectedRecipe={this.state.selectedRecipe} recipe={recipe} id={index}/>
                })}
                <Generate/>
            </div>
        )
    }
}

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

        this.setThisAsSelected = this.setThisAsSelected.bind(this)
    }

    setThisAsSelected() {
        this.props.setRecipeAsSelected(this.props.id)
    }

    render() {
        return (
            <div onClick={this.setThisAsSelected} key={this.props.id} id={this.props.id} className={this.props.selectedRecipe === this.props.key ? "bg-primary" : "test"}> {this.props.recipe} </div>
        )
    }
}

class Generate extends React.Component {
    render() {
        return (
            <div>
                <button>GENERATE</button>
            </div>
        )
    }
}

document.addEventListener('DOMContentLoaded', () => {
    ReactDOM.render(
        <App/>,
        document.body.appendChild(document.createElement('div')),
    )
});
1
Each child in an array or iterator should have a unique "key" prop To fix this, add the key in your RecipleList's render method, when mapping through to the <Recipe /> components. ie, <Recipe ... key={index} /> - sme
where does class="unselected" come from ? It is not in the code. - leaf
I guess it should be "test". I changed it for clarity but forgot to update it on the element - Jonny B
Resolution in update above - Jonny B
@JonnyB just updated answer to include added detail as requested :-) - Dacre Denny

1 Answers

2
votes

This kind of issue can be caused by the key prop not being rendered on list items (ie as the warning reads). The reason key is important here is that React uses the unique per-item key prop to correctly resolve/determine how to update items rendered in a list (ie via map()) during the render cycle.

One easy way to incorporate the key prop is to make use of the unique "index" passed in from the map callback, as follows:

render() {
    return (
        <div>
            {this.state.recipes[0]["recipes"].map((recipe, index) => {
                return <Recipe 
                        setRecipeAsSelected={this.setRecipeAsSelected}
                        selectedRecipe={this.state.selectedRecipe} 
                        recipe={recipe} id={index} key={index }/> 
            })}
            <Generate/>
        </div>
    )
}

Update

Also, the reason you don't seeing the onClick attribute in the rendered HTML (ie as can be seen in dev-tools), is because the rendered HTML that dev-tools shows is not the actual JSX that you defined in your component's render() method(s).

The JSX that you define is actually translated to javascript behind the scenes, and as a result, the onClick handler gets attached to the corresponding javascript Node object for that <div /> (this is internally done via addEventListener()). This therefore makes rendering the onClick handler into the HTML markup redundant.

Hope that helps!