Problem rendering the contents of array using map on reactjs - arrays

I use axios to get the data from the server and store the received data into the array setCountries. This part works.
Code in codesandbox
Then, I simply want to render the whole list of country names contained on the array using map.
I am making some mistake there, because I get the error
TypeError: setCountries.map is not a function
The error comes from this part of the code.
Where is the error coming from?
const [countries, setCountries] = useState([])
const showCountries = () => {
return (
<div>
<ul>
{setCountries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}
</ul>
</div>
)
}
return (
<div>
<div>
<h1>Countries</h1>
{showCountries()}
</div>
</div>
);
}
export default App;

You're using function setCountries instead of array countries
Corrected:
<ul>
{countries.length > 0 && countries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}
</ul>
Also use null checks to avoid other issues

setCountries is a function for setting the country's state. It does not have the map function.
The solution here will be to use countries.map instead:
const showCountries = () => {
return (
<div>
<ul>
{countries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}
</ul>
</div>
)
}

setCountries is a function and that's why you can't use a map. If you try the code I wrote below, your problem will be resolved.
{countries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}

Related

Expression statement is not assignment or call when looping through a list and trying to create a grid layout of components

I am trying to create a grid layout of video components but my IDE gives me a warning saying
Expression statement is not assignment or call
import React, {Fragment} from 'react';
import VideoClip from "../Video/VideoClip";
function SubjectView(props) {
let ids = ["RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd6iZHeEknCkY00",
"RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd2ipHeEknCkY00",
"RfKHsvF69Vdjdiu6bdugsyRcjYpQXrpKd2iZHeEknCkY00"
];
return (
<Fragment>
<div className="columns-3" >
{ids.map((id)=>{
<VideoClip id={id}/>
console.log(id)
})}
</div>
</Fragment>
);
}
export default SubjectView;
I see the IDs printed in the console but nothing renders.
The video component looks like
function VideoClip() {
let { id } = useParams();
return (
<div className="container mx-auto px-4">
<MuxPlayer
streamType="on-demand"
playbackId={id}
metadata={{
video_id: "video-id-54321",
video_title: "Test video title",
viewer_user_id: "user-id-007",
}}
/>
</div>
);
}
export default VideoClip
I am wondering if I am trying to create the components incorrectly. Is there a best practice when trying to achieve this?
You're not returning any value from ids.map
<>
<div className="columns-3" >
{ids.map((id)=><VideoClip id={id}/>)}
</div>
</>
One issue is you are not returning <VideoClip id={id}/> in map function in jsx. Also, if map is used - key needs to be set
return (
<Fragment>
<div className="columns-3">
{ids.map((id) => {
console.log(id);
return <VideoClip key={id} id={id} />;
})}
</div>
</Fragment>
);
Next issue is about VideoClip component parameter. Id needs to be extracted in that way in case it is passed as an attribute. Also, memoize the objects that you are passing down to a components. Metadata here, for example.
function VideoClip({ id }) {
const metadata = useMemo(() => {
return {
video_id: "video-id-54321",
video_title: "Test video title",
viewer_user_id: "user-id-007"
};
}, []);
return (
<div className="container mx-auto px-4">
<MuxPlayer streamType="on-demand" playbackId={id} metadata={metadata} />
</div>
);
}
Last thing - wrap your array in useMemo so this array will not cause crazy rerenders.
const ids = useMemo(
() => [
"RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd6iZHeEknCkY00",
"RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd2ipHeEknCkY00",
"RfKHsvF69Vdjdiu6bdugsyRcjYpQXrpKd2iZHeEknCkY00"
],
[]
);
Note: you will see x2 logs in the console in the Codesandbox due to <StrictMode> is set.

Array is showing empty initially, and I believe it's causing ".map is not a function" error

I am attempting to fetch some data from a strapi api and I'm running into an issue where it's stating that .map is not a function of badgeCategories.
Here is a sample of the code which fetches the API and updates badgeCategories.
export default function Home({ posts }) {
const [badgeCategories, setbadgeCategories] = useState([]);
useEffect(() => {
const fetchBadgeCatData = async () => {
const badgeCatResult = await api.getBadgeCategories();
setbadgeCategories(badgeCatResult.data);
};
fetchBadgeCatData();
}, [])
I attempted to console log badgeCategories and noticed that the array is initially empty on first run.
{console.log(badgeCategories)}
When the code gets to
{badgeCategories.map((g) => (
<div>
I get an error thrown stating
TypeError: badgeCategories.map is not a function
My assumption is that the error is being thrown because the array is initially showing as empty so it cannot process the .map function; however I'm kind of stuck and have not been able to resolve this yet. Any help would be greatly appreciated and I'm sure it's something simple I'm missing.
Full Map Body:
{badgeCategories.map((g) => (
<div>
<div className="pt-6 pb-4">
<h1 className="text-2xl font-extrabold leading-4 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-2xl md:leading-9">
{g.attributes}
</h1>
<p className="text-md text-gray-500 dark:text-gray-400">
{g.description}
</p>
</div>
<div className="container">
<div className="flex flex-wrap">
<ul className="w-full space-y-2">
{itemData.filter(item => item.group == g.id).map(filteredItem => (
<li key={filteredItem.order}>
<Item
type={filteredItem.type}
text={filteredItem.text}
color={filteredItem.color}
href={filteredItem.href}
hrefActive={filteredItem.hrefActive}
/>
</li>
))}
</ul>
</div>
</div>
</div>
))}
You are correct that the array is empty initially. However, this should not throw an error as you set an initial value for the array in your useState call:
const [badgeCategories, setbadgeCategories] = useState([]);
// here ^^
What could be causing an issue is actually not when the data has not loaded but when the data already has loaded. Based on the screenshot of your console, it seems that the new data is not an array.
[] // before the data loads, `badgeCategories` is an empty array.
{ data: Array(2), meta: {...} } // after it loads, `badgeCategories` is an object
// containing a `data` property of an array.
This seems to be coming from this line of code:
const badgeCatResult = await api.getBadgeCategories();
setbadgeCategories(badgeCatResult.data); // NOT an array!
It seems that badgeCatResult.data is not an array, but an object containing a data property that is an array. Try using this instead:
const badgeCatResult = await api.getBadgeCategories();
setbadgeCategories(badgeCatResult.data.data); // This should work!
You can simply avoid the error by checking badgeCategories is a valid Array before calling map
{Array.isArray(badgeCategories) && badgeCategories.map((g) => (
<div>
.....
)
}

React - Each child in a list should have a unique 'key' prop

As my first react project, I decided to try and make a Pokedex.
I have an array of Pokemon and that I pass into a List component and use the .map() function to render. I understand that the root-most element of that the .map() function returns needs a unique key and I understand that it is ideal if the key can be truly unique to the item so that if the list is sorted, items have the same key. Therefore, I figured using the 'id' of the pokemon would be ideal. I believe I have done that but I cannot get rid of the warning in console. I'd really appreciate a hand with this.
export default class List extends React.Component {
render() {
const { list, nav } = this.props;
return (
<div className="list">
{list.map((pokemon) => (
<PokemonItem key={pokemon.id} navigation={nav} pokemon={pokemon} />
))}
</div>
);
}
}
PokemonItem Render Method
render() {
const { pokemon, navigation } = this.props;
return (
<div onClick={() => {
navigation.navigate("Details", { pokemon });
}}
className={"list-item bg_" + pokemon.types[0]}>
<div className="header">
<div className="name">{pokemon.name}</div>
<div className="id">#{this.getId(pokemon.id)}</div>
</div>
<div className="body">
<div className="types">
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
</div>
<div className="sprite">
<img src={pokemon.imgURL} alt={pokemon.name} title={pokemon.name}></img>
</div>
</div>
<div className="background-image">
<img src={PkBall} alt="" />
</div>
</div>
);
}
Warning message showing in console
Checking your PokemonItem it reveals that the reason may be laying in this piece of code:
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
This is easily fixed by adding the key attribute:
{pokemon.types.map((type) => {
return <div className="type" key={type.id}>{type}</div>;
})}
You need to add a key in every item returned from a map in order to avoid this error. Also I advice you to add the console output related to your question in the body so it's easier to pinpoint the errors.
After the edit of the OP's question the warning occurs here:
<div className="types">
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
</div>
The key-property is not set for div and should be done like in the first method. If type is unique you can use this as key.

map is not a function on array React

I'm getting an error that map is not a function on my data.
I'm getting a response back from an api that is returning an array of objects. When I don't refresh the page I can view the results displayed just fine and even navigate to view them individually (when I click on see more). However, when I refresh the page I get the
error of "Map is not a function" on my props even though the results are displaying in the console log.
I'm lost here and can't figure out why it's doing that.
componentDidMount() {
this.props.getCanyons();
}
render() {
const { canyons } = this.props;
console.log(canyons)
return (
<section>
{canyons.map(canyon => (
<section key={canyon.canyon_id}>
<h3>{canyon.canyon_name}</h3>
<img src={canyon.canyon_pic} alt={canyon.canyon_name} />
<Link key={canyon.canyon_id} to={`/canyon/${canyon.canyon_id}`}>
<button>See More</button>
</Link>
</section>
))}
</section>
);
}
}
When the api failed or having lag time to get response, it may be undefined. This kind of checking prevent you to from such problem.
return (
{canyons && canyons.map(canyon => (
...skipped code
))}
)
Typescript provide feature of adding a ? before try to access the related Object type variable
//In typescript
{canyons?.map(canyon => (
...skipped code
))}
componentDidMount() {
this.props.getCanyons();
}
render() {
const { canyons } = this.props;
console.log(canyons)
return (
<section>
{ canyons !== '' || canyons.length > 0 ? //change here
canyons.map(canyon => (
<section key={canyon.canyon_id}>
<h3>{canyon.canyon_name}</h3>
<img src={canyon.canyon_pic} alt={canyon.canyon_name} />
<Link key={canyon.canyon_id} to={`/canyon/${canyon.canyon_id}`}>
<button>See More</button>
</Link>
</section>
))
:
null
}
</section>
);
}
}
Please follow the change. It should works for you...
Many browsers provide a live view when using console.log(). When the request not finished 'canyons' is undefined. Use
console.log(JSON.parse(JSON.stringify(obj)))
For this problem, try to set default value or check variable first

Create a custom Hook for splitting strings (React JS)

I'm working with React JS (hooks) for recently. For a project, I need to split many strings in different divs. So, I created a function to this, that saves me some time! My actual question is : Should I create a custom Hook instead of my function ?
Of course, the actual code works, but as a beginner, I don't know if my way is clean. I need feedbacks cause my main goal to write the best and clear code as possible.
// Splitting Texts
const splitText = str => {
return (
<div>
{str.split(' ').map((item, index) => {
return (
<div key={index}>
{item}
</div>
);
})}
</div>
);
};
// Main App
export default function App() {
const name = splitText('Lucie Bachman');
const description = splitText('Hey, this is my first post on StackOverflow!');
return (
<div className="App">
<h1>{name}</h1>
<h2>{description}</h2>
</div>
);
}
Expected results :
<h1>
<div>
<div>Lucie</div>
<div>Bachman</div>
</div>
</h1>
I'm super excited to have joined the community!
Thanks to you, and take care.
Lucie Bachman
A custom hook is something that uses out of the box react hooks to actually provide a logic and return data.
If the function returns JSX, its actually just a function or can be used as a functional component
Since you only want to split string once you can convert it into a component and use React.memo to optimize rendering
// Splitting Texts
const SplitText = React.memo(({str}) => {
return (
<div>
{str.split(' ').map((item, index) => {
return (
<div key={index}>
{item}
</div>
);
})}
</div>
);
});
// Main App
export default function App() {
return (
<div className="App">
<h1><SplitText str={'Lucie Bachman'} /></h1>
<h2><SplitText str={'Hey, this is my first post on StackOverflow!''} /></h2>
</div>
);
}

Resources