Dust_In_The_Wind
Dust_In_The_Wind

Reputation: 3692

ReactJS rendering a collection of children throws "Invariant Violation: Objects are not valid as a React child" error

I'm making a RecipeBox App in React, it works fine until I try to implement a search functionality. The error is thrown as soon as the user enters a search item in the search bar. The else block in this render method is what throws the error -

render() {
        var gridRecipes = this.props.recipes;
        let onDelete = this.props.onRecipeDelete;


        //second set of props
        let onRecipeNameEdit = this.props.onRecipeNameEdit;
        let onRecipeIngEdit = this.props.onRecipeIngredientsEdit;
        let onRecipeTagsEdit = this.props.onRecipeTagsEdit;
        let onRecipeEditSubmit = this.props.onRecipeEditSubmit;
        let onRecipeEditSelect = this.props.onRecipeEditSelect;

        let filterNameEdit = this.props.filterNameEdit;
        let filterIngredientsEdit = this.props.filterIngredientsEdit;
        let filterTagsEdit = this.props.filterTagsEdit;

        //props for search
        let searchedItem = this.props.searchItem;


        console.log(gridRecipes);
        var recipeCards = [];

        if(searchedItem === "") {
            recipeCards = gridRecipes.map(function (recipe, index) {
                return <div className="recipe-item" key={index}>
                    <RecipeCard recipe={recipe} key={index} index={index} onDelete={onDelete}
                                onRecNameEdit={onRecipeNameEdit}
                                onRecIngredientsEdit={onRecipeIngEdit}
                                onRecTagsEdit={onRecipeTagsEdit}
                                onRecEditSubmit={onRecipeEditSubmit}
                                filterNameEdit={filterNameEdit}
                                filterIngredientsEdit={filterIngredientsEdit}
                                filterTagsEdit={filterTagsEdit}
                                onRecipeEdit={onRecipeEditSelect}
                    />
                </div>
            });
        }

        else {
            console.log(`user searched for ${searchedItem}`);
            recipeCards = gridRecipes.filter(function (recipe, index) {
                //console.log(recipe);
                if(recipe.name.toLowerCase().indexOf(searchedItem) !== -1) {
                    return <div className="recipe-item" key={index}>
                        <RecipeCard recipe={recipe} key={index} index={index} onDelete={onDelete}
                                    filterNameEdit={filterNameEdit}
                                    filterIngredientsEdit={filterIngredientsEdit}
                                    filterTagsEdit={filterTagsEdit}
                        />
                    </div>
                }
            })
            console.log(recipeCards);
            console.log(Array.isArray(recipeCards));
        }

        return (
            <div className="flex-container">
                {recipeCards}
            </div>
        )
    }

Here's the error message -

Uncaught Error: Objects are not valid as a React child (found: object with keys {name, ingredients, tags}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `RecipeFlexContainer`.

enter image description here Looking at the error message and reading another similar question, it appears that the collection of children is being rendered as an individual object/collection of objects, as opposed to an array containing the objects. But that is not the case. I've got two console.log statements in the code to verify this -

console.log(recipeCards);
console.log(Array.isArray(recipeCards));

The console shows -

[Object]
true

Which shows that recipeCards is a an array containing object(s). But then the return throws off the error -

return (
            <div className="flex-container">
                {recipeCards}
            </div>
        )

Interestingly, the same return statement works fine, without throwing any errors, if I remove the search bar functionality and delete the else block entirely -

else {
            console.log(`user searched for ${searchedItem}`);
            recipeCards = gridRecipes.filter(function (recipe, index) {
                //console.log(recipe);
                if(recipe.name.toLowerCase().indexOf(searchedItem) !== -1) {
                    return <div className="recipe-item" key={index}>
                        <RecipeCard recipe={recipe} key={index} index={index} onDelete={onDelete}
                                    filterNameEdit={filterNameEdit}
                                    filterIngredientsEdit={filterIngredientsEdit}
                                    filterTagsEdit={filterTagsEdit}
                        />
                    </div>
                }
            })
            console.log(recipeCards);
            console.log(Array.isArray(recipeCards));
        }

Here's my app on Codepen.

Steps to reproduce -

  1. Search for an existing recipe name in the search bar, like "garlic"
  2. Pull up the console

This is in the RecipeFlexContainer component.

Can anyone explain what's going on?

Upvotes: 3

Views: 1871

Answers (1)

Sam R.
Sam R.

Reputation: 16450

Change the filter to map and you'll have it fixed:

// after
recipeCards = gridRecipes.map(function (recipe, index) {

// before
recipeCards = gridRecipes.filter(function (recipe, index) {

But a cleaner way would be:

gridRecipes
  .filter(r => r.name.toLowerCase().indexOf(searchedItem) !== -1)
  .map((r, i) => (
    <div className="recipe-item" key={i}> 
      <RecipeCard /> 
    </div>)
  );

Upvotes: 3

Related Questions