2
votes

Codesandbox link.

I'm getting this error when trying to use filter() through a big array of objects (defined as 'filteredCharacters'), and render only those match the id of '6' to the screen (only one does).

I console.log(filteredCharacters), and I can clearly see in console that it works. But for some reason, I'm getting a "Objects are not valid as a React child" error thrown.

The code below is from /components/content.js, in the Codesandbox link above.

import React, { Component } from 'react';
import Intro from '../intro/intro';

class Content extends Component {
  render() {
    // Grab the 'characters' object from App.js, and assign it to 'this.props'
    const { characters } = this.props;

    // Filter the chracters and return only whose 'id' belongs to that of '6'
    const filteredCharacters = characters.filter(characters => {
      if (characters.id === 6) {
        return (
          <div className="characters" key={characters.id}>
            <p>Name: {characters.Name}</p>
            <p>ID: {characters.id}</p>
            <p>Job: {characters.Job}</p>
            <p>Age: {characters.Age}</p>
            <p>Weapon: {characters.Weapon}</p>
            <p>Height: {characters.Height}</p>
            <p>Birthdate: {characters.Birthdate}</p>
            <p>Birthplace: {characters.Birthplace}</p>
            <p>Bloodtype: {characters.Bloodtype}</p>
            <p>Description: {characters.Description}</p>
          </div>
        )
      }
    });

    // Check to see if it logs properly (it does)
    console.log(filteredCharacters);

    // When trying to render this to the screen below, it doesn't work

    return (
      <div>
        {filteredCharacters}
      </div>
    )
  }
}

export default Content;
3
filter takes in a predicate that returns a boolean to decide what elements to keep if true, or to skip if false - Karen Grigoryan
members of filteredCharacters array are objects and this error comes while rendering those objects - ashish singh

3 Answers

2
votes

filter will only create a new array with all the elements that returned a truthy value from the function.

You can instead use filter first to get the relevant characters, and then use map on the new array to get the JSX you want to render.

const filteredCharacters = characters
  .filter(character => character.id === 6)
  .map(character => (
    <div className="characters" key={character.id}>
      <p>Name: {character.Name}</p>
      <p>ID: {character.id}</p>
      <p>Job: {character.Job}</p>
      <p>Age: {character.Age}</p>
      <p>Weapon: {character.Weapon}</p>
      <p>Height: {character.Height}</p>
      <p>Birthdate: {character.Birthdate}</p>
      <p>Birthplace: {character.Birthplace}</p>
      <p>Bloodtype: {character.Bloodtype}</p>
      <p>Description: {character.Description}</p>
    </div>
  ));
1
votes

Adding to @Tholle's answer, you could combine those operations into one with reduce

const filteredCharacters = characters
  .reduce((acc, character)  => {

    if (character.id !== 6) return acc;

    acc.push(<div className="characters" key={character.id}>
      <p>Name: {character.Name}</p>
      <p>ID: {character.id}</p>
      <p>Job: {character.Job}</p>
      <p>Age: {character.Age}</p>
      <p>Weapon: {character.Weapon}</p>
      <p>Height: {character.Height}</p>
      <p>Birthdate: {character.Birthdate}</p>
      <p>Birthplace: {character.Birthplace}</p>
      <p>Bloodtype: {character.Bloodtype}</p>
      <p>Description: {character.Description}</p>
    </div>);

    return acc;
  }, []);
0
votes

Currently you are using one simple object to behave as a node in the HTML structure of the component you are writing. One of the best practices to use react in such cases is to create and then call this as a react component itself.

Following is your code that now has a separate component that can be called on need:

import React, { Component } from 'react';
import Intro from '../intro/intro';

const FilteredCharcters = characters => {
  characters.filter(character => {
   if (character.id === 6) {
     return (
       <div className="characters" key={character.id}>
         <p>Name: {character.Name}</p>
         <p>ID: {character.id}</p>
         <p>Job: {character.Job}</p>
         <p>Age: {character.Age}</p>
         <p>Weapon: {character.Weapon}</p>
         <p>Height: {character.Height}</p>
         <p>Birthdate: {character.Birthdate}</p>
         <p>Birthplace: {character.Birthplace}</p>
         <p>Bloodtype: {character.Bloodtype}</p>
         <p>Description: {character.Description}</p>
       </div>
     )
   }
 });

class Content extends Component {

  render() {
  const { characters } = this.props;
    return (
      <div>
        <FilteredCharacters characters={characters} />
      </div>
    )
  }
}

export default Content;