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>