Home > Software engineering >  React iterate nested arrays/objects with map. Is return mandatory to render each map function?
React iterate nested arrays/objects with map. Is return mandatory to render each map function?

Time:11-12

I'm new to React. I'm building a small function component that renders the weaknesses of a Pokemon.

It takes the type of the Pokemon stored in the state (pokemonState.types) as an array of objects for each type, compare the type to a JSON file containing all the type effectiveness (see below).

types_data.json

  "electric": {
"attack": {
  "double": ["flying", "water"],
  "half": ["dragon", "electric", "grass"],
  "zero": ["ground"]
},
"defense": {
  "half": ["electric", "flying", "steel"],
  "double": ["ground"],
  "zero": []
}

To get the data I want, I need to do several iterations and I noticed my component only renders when I specify the return keyword and wrap my element in a HTML tag.

Function that doesn't render anything (but console.log doesn't return undefined)

  const pokemonTypeChart = () => {
if (!_.isEmpty(pokemonState)) {
  const allTypes = Object.entries(types_data);
  const typeEffectiveness = allTypes.map(([key, value]) => {
    pokemonState.types.map((el) => {
      if (el.type.name === key) {
        console.log(key, value);
        return <p>{el}</p>;
      }
    });
  });
  return (
    <div>
      <h1>Test</h1>
      <p>{typeEffectiveness}</p>
    </div>
  );
}};

Function that works well:

const pokemonTypeChart = () => {
if (!_.isEmpty(pokemonState)) {
  const allTypes = Object.entries(types_data);
  return (
    <div>
      <h1>Type Effectiveness</h1>
      {allTypes.map(([key, value]) => {
        return (
          <>
            {pokemonState.types.map((el, i) => {
              if (el.type.name === key)
                return <h3 key={i}>{value.defense.double}</h3>;
            })}
          </>
        );
      })}
    </div>
  );
}};

My question are:

  • For the function that doesn't render anything, the problem is that I don't use return after each iteration right?
  • Is my second function good practice in this scenario? is there a cleaner way I should I have wrote it?

Thanks for your help.

CodePudding user response:

The reason why your first function isn't working is that you are returning <p>{el}</p>.

el is equal to an object & you are expecting a string.

Specifically, in this case, it is something like {type:{name: "electric"}

If you change that line from return <p>{el}</p>; to return <p>{el.type.name}</p>;, then that fixes our inner .map, so you are getting closer.

However, you've also got a second problem... the outer .map function is not returning anything. Your typeEffectiveness variable is what we're ultimately rendering, and that variable is equal to the result from calling allTypes.map. While your nested pokemonState.types.map function is getting returned to the parent .map function, your parent .map function does not have a return statement.

Ultimately, you are rendering {typeEffectiveness}, so we've gotta make sure that variable is an array of react elements that will render to the DOM.

There were a couple other minor issues as well. Namely, we have to remove the paragraph tags <p>{typeEffectiveness}</p> here or we'd end up with nested p tags, since each item in the typeEffectiveness list is already enclosed in <p></p>.

Finally, you should add key tags to your list items as well. <p key="{el.type.name}">{el.type.name}</p>

Dig into the documentation on Lists & Keys here: https://reactjs.org/docs/lists-and-keys.html

The following code is a debugged version of your first function.

I also included a refactored version of your section function below. It would be fairly inefficient to map through the entire types_data object entries, when we really only care about the types present in our pokemonState, so take a look at my approach to that below as well.

const e = React.createElement;

const pokemonState ={types: [{type:{name: "electric"}}]};
let types_data = {
    "electric": {
        "attack": {
            "double": ["flying", "water"],
            "half": ["dragon", "electric", "grass"],
            "zero": ["ground"]
        },
        "defense": {
            "half": ["electric", "flying", "steel"],
            "double": ["ground"],
            "zero": []
        }
    }
};


const PokemonTypeChart = () => {
    if (!_.isEmpty(pokemonState)) {
        const allTypes = Object.entries(types_data);
        const typeEffectiveness = allTypes.map(([key, value]) => {
            return (pokemonState.types.map((el) => {
                if (el.type.name === key) {
                    return (
                    <p key="{el.type.name}">{el.type.name}</p>
                    );
                    
                }
            }));
        });
        return (
            <div>
                <h1>Test</h1>
                {typeEffectiveness}
            </div>
        );
    }};




const domContainer = document.querySelector('#pokemon');
ReactDOM.render(e(PokemonTypeChart), domContainer);
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>
    
    <div id="pokemon"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

const e = React.createElement;

const pokemonState ={types: [{type:{name: "electric"}}]};
let types_data = {
    "electric": {
        "attack": {
            "double": ["flying", "water"],
            "half": ["dragon", "electric", "grass"],
            "zero": ["ground"]
        },
        "defense": {
            "half": ["electric", "flying", "steel"],
            "double": ["ground"],
            "zero": []
        }
    }
};


const PokemonChart = () => {
    if (!_.isEmpty(pokemonState)) {
        const plistItems = pokemonState.types.map((item) => 
            types_data[item.type.name].defense.double.map((i) => 
                <p key={i}>{i}</p>
            ));
        return (
            <div>
                <h1>Type Effectiveness</h1>
                {plistItems}
            </div>
        );
    }};
const domContainer = document.querySelector('#pokemon');
ReactDOM.render(e(PokemonChart), domContainer);
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>
    
    <div id="pokemon"></div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related