Error : Cannot read property 'map' of undefined React.JS - javascript

I'm following the react js tutorial, and I keep running into this issue
import React from "react";
import NewsCard from "../NewsCard/NewsCard";
const NewsCards = ({ articles }) => {
return (
<div>
{articles.map((article, i) => {
<NewsCard />;
})}
</div>
);
};
export default NewsCards;

Seems like your articles does not have default value as [].
You can change as follow. And you should give key attribute when using map function.
const NewsCards = ({ articles }) => {
const data = articles ? articles : []
return (
<div>
{data.map((article, i) => {
<NewsCard key={article.id}/>;
})}
</div>
);
};

Probably articles is not initialized when you try to map throught it. Try this:
{articles?.map((article, i) => {
<NewsCard />;
})}
OR
{articles && articles.map((article, i) => {
<NewsCard />;
})}
</div>
That way you will first make sure if articles exist

This means that the articles prop is undefined.
There are several ways to solve this. The first and easiest way is by implementing the following logic:
{articles?.length ? articles.map((article, i) => <NewsCard />) : "There are no articles here."}
Another way to solve this is by implementing React proptypes - you can read about this here.
Third and "hardest" (but probably best) way to solve this is by using a static type checking tool. Flow comes to mind, but you can use TypeScript too.

If you still need help, just like what the previous answers said, make sure that articles is initialized/defined by using the && operator to make that check. Also, based upon what you wrote, the map method is returning undefined since you specified a function body (using the function body bracket notation {} ) without a return statement. So instead write the map method like this:
<div>
{articles && articles.map((article, i) => <NewsCard />)}
</div>
or like this:
<div>
{articles && articles.map((article, i) => {
return <NewsCard />
})}
</div>
The first example implies an implicit return since an arrow function is being used and a function body is not present (there are no function body brackets { }).

Related

Accessing array indices in React

I am new to React and Javascript as a whole. I am simply trying to access array indices after a fetch. I can log a specific index after a file update, but when I refresh the page, I get an 'Undefined' error. I feel as if I am doing something wrong with my state, but I don't know enough about React to debug this. if I attempt to access " users[0].name", I get the "TypeError: Cannot read properties of undefined (reading 'name')
(anonymous function).
import React, { useEffect, useState } from "react";
import { Container, Card, Divider, Typography } from "#mui/material";
const RestData = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users/")
.then((response) => response.json())
.then((data) => setData(data));
}, []);
return (
<Container>
{data.map((users) => {
return (
<Card
data={data}
key={users.id}
sx={{ width: "300px", mt: 2, background: "#ff9800" }}
>
<Typography variant="h6" color="seagreen">
{users.name}
</Typography>
<Divider />
{users.email}
<Divider />
{users.company.name}
<Divider />
{users.phone}
<Divider />
{users.website}
<Divider />
</Card>
);
})}
</Container>
);
};
export default RestData;
Although I don't see this occurring in the code you posted, you are getting that particular error because you are trying to access .name when the object
being accessed is undefined.
I presume you are trying to do something like data[0].name.
The issue is, your data array starts empty, so if you try to access any index of an empty array, it will be undefined, you will get that error.
The solution is to make sure the data array already has it's elements in before you access an index of it.
Now, there are many ways to do this. Because it doesn't seem to be a part of your code, it is hard to tell you the best way. But here is a couple:
1- Do an if check before attempting to access the property:
if (data[0]) {
console.log(data[0].name)
// do stuff
}
2- Check if the property exists first using the && operator:
For this case, the attempt to access the name is only done if data[0] is truthy.
console.log(data[0] && data[0].name)
3- Use optional chaining. This is the better one imo, since it allows you to skip the if and will only attempt to access a property if it is not undefined.
console.log(data[0]?.name)
So now to put this in practice, here is a sample app:
const App = () => {
const [data, setData] = React.useState([]);
React.useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users/")
.then((response) => response.json())
.then((data) => setData(data));
}, []);
console.log("data", data);
return (
<div>
<div>
<h3>Example by accessing the index directly with Optional Chaining:</h3>
<p>{data[0] && data[0].name}</p>
</div>
<div>
<h3>Example with the map</h3>
{(data).map((d) => {
return <p>{d.name}</p>;
})}
</div>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Since you're fetching the data at rendering time you don't have users[0].name, so what I would suggest is to use the conditional operator ?.
So you have something like this
data[0]?.name
What happens in this case is that typescript try to access the property name at the given index, if the property is undefined returns null otherwise returns the value.
I've also made a quick sandbox for demonstration.

Should functions within React functional components be wrapped?

I have a functional component that has one function within it, renderMessages.
const MessageContainer = (props) => {
const renderMessages = () => {
return props.messages.map((message, index) => {
return(
<Message
key={index}
username={message.username}
message={message.message}
fromCurrentUser={message.fromCurrentUser}
/>);
})
}
return(
<div className='messages'>
{renderMessages()}
</div>
)
}
However, I realized that instead of wrapping renderMessages function on the map, I can just have:
const renderMessages = props.messages.map((message, index) => {
return(
<Message
key={index}
username={message.username}
message={message.message}
fromCurrentUser={message.fromCurrentUser}
/>);
})
}
And as a result, my final return would just contain
return(
<div className='messages'>
{renderMessages}
</div>
)
In a class-based component and within a render function, I'd use the last of the two. Which of the two is considered the best practice when using functional components, and why?
EDIT:
Which of the two is considered the best practice when using functional components, and why?
Best practices change with context - e.g. the team you're working on - so this is an opinion-based question out of the gate.
That being said, in my opinion, I wouldn't do either. I'd do (and I do) this:
const MessageContainer = (props) => {
return (
<div className='messages'>
{props.messages.map((message, index) => (
<Message
key={index}
username={message.username}
message={message.message}
fromCurrentUser={message.fromCurrentUser}
/>
))}
</div>
)
}
What's the purpose of the extra variable anyway?
While you're at it, don't use indexes for keys
The dirty secret about all those extra methods you stuck on your class components that encapsulated rendering logic is that they were an anti-pattern - those methods were, in fact, components.
EDIT #2
As pointed out in the other answer, the most performant solution for this specific use case is specifying the map function outside the functional component:
const renderMessage = (message,index) => (
<Message
key={index}
{...message}
/>
)
const MessageContainer = (props) => {
return (
<div classname='messages'>
{props.messages.map(renderMessage)}
</div>
);
}
But, you shouldn't prematurely optimize and I would advocate for the original solution I posted purely for simplicity/readability (but, to each their own).
Good job separating the mapping outside the component's return, because this way you'll be only calling the same function over and over again untill the .map is done iterating, but if you wrote it in the component's return, every time the .map iterate over the next item you'll be creating a new function.
Regarding the question, I'd recommend the second way, clean/readable code is always preferable.
P.S. try to use the unique message id instead of the index.

React key prop within wrapper

While looking through our code base, I found code that looks a bit like this:
const Carousel = ({ items }) => {
return (
<CarouselOuter>
{items.map((item) => (
<CarouselItemWrapper>
<CarouselItem key={item.key}>
...
</CarouselItem>
</CarouselItemWrapper>
)}
</CarouselOuter>
);
}
Notice that the key prop is on CarouselItem, not CarouselItemWrapper, the component that's directly returned from items.map. This seems to work fine, and there are no warnings in the console, but it runs counter to every example I've seen using map in React.
I want know if there's a good argument (specifically in regards to performance) for rearranging the code with the key as shown below, or if this is just a stylistic choice:
const Carousel = ({ items }) => {
return (
<CarouselOuter>
{items.map((item) => (
<CarouselItemWrapper key={item.key}>
<CarouselItem>
...
</CarouselItem>
</CarouselItemWrapper>
)}
</CarouselOuter>
);
}
Side note: CarouselOuter, CarouselItem, and CarouselItemWrapper are all styled-components, but I doubt that's relevant.

Mapping from passed props in a functional component

I am building a website that is reliant on a json file for all of its information.
In my app.js the json info is showing properly when I console.log() it, but when I try and pass it to my functional components it is giving me undefined.
in app.js
<Route
exact
path="/puppies"
render={props => (
<Puppies {...props} propdata={this.state.propdata} />
)}
/>
This seems to be working fine, however when I try and map it inside the component it tells me that its undefined.
function Puppies(propdata) {
return <div>{propdata.puppies.map(puppies =>
<h1>{puppies.name}</h1>
)}</div>;
}
I have done this before but with a class component. So most likely I am making a mistake with the functional component.
The full code is viewable here:
https://github.com/Imstupidpleasehelp/Puppywebsite/tree/master/src
Thank you for your time.
You'll probably need to check that the data is null of undefined. You are passing a big object with data, I recommend to pass more specific props instead of a big object.
I like to prevent my data to be undefined in 2 ways:
lodash.get
Optional Chaining
Usage:
import _ from 'lodash';
function Puppies({ propdata }) {
const puppies = _.get(propdata, 'puppies', []);
return (
<div>
{puppies.map(puppies => <h1>{puppies.name}</h1>)}
</div>
);
}
or
function Puppies({ propdata }) {
const puppies = propdata?.puppies || [];
return (
<div>
{puppies.map(puppies => <h1>{puppies.name}</h1>)}
</div>
);
}
What you have as propdata is actually just an object containing all properties that you have passed in. You should use destructuring to get the actual propdata value.
Solution:
function Puppies({propdata}) {
return (
<div>
{propdata.puppies.map(puppies =>
<h1>{puppies.name}</h1>
)}
</div>
);
}
Since this is asynchronous request to get the data, your data is not readily available hence you need to handle that scenario.
function Puppies(propdata) {
return (
{
propdata.puppies.length>0 ? <div>
propdata.puppies.map((puppies)=>{
<h1>{puppies.name}</h1>
})
</div> :null
}
)

How can I use map to return a nested object?

What I want to do, using map, is pretty plain.
I want to call this:
<Striper size={3} text={"Hey everybody!"}/>
To get this:
<>
<Stripe>
<Stripe>
<Stripe>
Hey everybody!
</Stripe>
</Stripe>
</Stripe>
</>
I tried this way, but it fails:
const Striper = (props) => {
const contentTop=props.sizer.map((item)=> <Stripe>)
const contentBottom=props.sizer.map((item)=> </Stripe>)
return (
<div>
{contentTop}
{contentBottom}
</div>
)
}
Basically only this works (which isn't what I want):
const contentTop = props.sizer.map((item)=> <Stripe></Stripe>)
How could I get what I want?
The solution ended up being really simple (thank you, Emile): use .reduce.
As it says in the documentation about reduce, it's really useful when you need only one thing returned. And that's what I needed.
As I said in a comment:
What I want to return from <App size={2} text="Hello"/> is really
<Stripe><Stripe>Hello</Stripe></Stripe>, but because I have to
return a whole object, the closest I can come with map is
<Stripe>Hello</Stripe><Stripe>Hello</Stripe>.
So instead, use reduce.
This is the solution, verified to work. (Note: I'm being somewhat handwavey about size={3} when it's actually an array, because I know how to do that stuff, it isn't my question but you can see my implementation in the answer below).
const Striper = (props) => {
const content = props.sizer.reduce(
(total, currentValue) => <Stripe color={props.colors}>{total}</Stripe>
)
return (
<div>
{content}
</div>
)
}
And as it's actually called:
const arr = Array(6).fill("this is my text!");
return (
<div>
<Striper sizer={arr} colors={colours}/>
</div>
);
I guess you can achieve something like with this approach:
import React from 'react';
import { render } from 'react-dom';
const Stripe = () => <span> – </span>
const App = ({ size, text }) => {
const arrFromNum = Array.from(Array(size), (x, i) => i + 1)
return (
<React.Fragment>
{arrFromNum.map(x => <Stripe />)}
{text}
{arrFromNum.map(x => <Stripe />)}
</React.Fragment>
)
}
render(<App size={4} text="Hello" />, document.getElementById('root'));
Does this answer your question?
Here's one possible way of many different possibilities based on your scenario:
<Striper size={3} text={"Hey everybody!"}/>
export default ({ size, text }) => <><Stripe size={size} /> {text} <Stripe size={size} /></>;
export default ({ size }) => <>{ Array(size).fill().map(_ => <h3>Stripe!</h3>) }</>;
https://stackblitz.com/edit/react-tnqv2k

Categories

Resources