Mapping from passed props in a functional component - javascript

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
}
)

Related

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

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 { }).

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.

Spread operator to pass all other props to a component. React.js

I'm having trouble understanding the spread operator when I want to pass all other props to a component.
Any help would be appreciated.
import React, { Fragment } from "react";
import SiteCard from "./SiteCard";
const SiteList = ({ sites }) => {
return (
<Fragment>
{sites.map((site) => {
return (
<SiteCard
key={site.login.uuid}
image={site.picture.large}
firstName={site.name.first}
lastName={site.name.last}
city={site.location.city}
country={site.location.country}
sensors={site.dob.age}
otherSiteProps={...site} // how can I pass the site props here?
/>
);
})}
</Fragment>
);
};
export default SiteList;
You are almost there with the solution.
You need to pass it as otherSiteProps={{...site}}.
This is if you want to pass site as an object to otherSiteProps property of SiteCard.
If you want to spread site and have multiple props for component SiteCard you do it like this:
<SiteCard
key={site.login.uuid}
image={site.picture.large}
firstName={site.name.first}
lastName={site.name.last}
city={site.location.city}
country={site.location.country}
sensors={site.dob.age}
{...sites}
/>
This in case that sites is an object. If site is an array, this wont work.
You just need to write:
<SiteCard
key={site.login.uuid}
image={site.picture.large}
firstName={site.name.first}
lastName={site.name.last}
city={site.location.city}
country={site.location.country}
sensors={site.dob.age}
{...site} // how can I pass the site props here?
/>
But wait, why you're making so complicated? You can just use:
<SiteCard {...site} />
Now, in your SiteCard component use required props.
And if I were you, I would not have separated SiteCard component for this scenario. I would just write:
{sites.map((site) => {
return (
// everything here I will utilize in html.
);
})}

How to pass React Component as props to another component

I am trying to call PopupDialog.tsx inside Content.tsx as a sibling of Item.tsx.
Previously PopupDialog.tsx is called inside C.tsx file but due to z index issue i am trying to bring it out and call it in Content.tsx
Is it possible to somehow pass the whole component(popupDialog and its parameters) in Content.tsx so that i could avoid passing back and forth the parameters needed for popupdialog in content.tsx.
Code in C.tsx where PopupDialog component is called.
const C = (props: Props) => (
<>
{props.additionalInfo ? (
<div className="infoButton">
<PopupDialog // need to take this code out and want to add in Content.tsx
icon="info"
callback={props.callback}
position={Position.Right}
>
<div className="popuplist">{props.additionalInfo}</div>
</PopupDialog>
</div>
) : (
<Button className="iconbutton"/>
)}
</>
);
Content.tsx where i would like to call PopupDialog.tsx with its parameters
const Content = (props: Props) => {
const [componentToRender, docomponentToRender] = React.useState(null);
const [isAnimDone, doAnim] = React.useState(false);
return (
<div className="ContentItems">
<PWheel agent={props.agent} />
{isAnimDone && (
<>
<Item {props.agent} />
{componentToRender &&
<PopupDialog/> //want to call here with all its parameters to be passed
}
</>
)}
</div>
);
};
Folder Structure
App.tsx
->ViewPort.tsx
->Content.tsx
->PWheel.tsx
->Item.tsx
->A.tsx
->B.tsx
->C.tsx
{props.additionalinfo &&
->PopupDialog.tsx
->PopupDialog.tsx
So if I understand the question correctly you want to pass one component into another so that you can use the properties or data of the passed componenet in your current component.
So there are three ways to achieve this.
1)Sending the data or entire component as prop.This brings disadvantage that even though components which don't require knowledge
about the passed component will also have to ask as a prop.So this is bascially prop drilling.
2)The other is you can use context api.So context api is a way to maintain global state variale.so if you follow this approach you don't need to pass data or componenet as props.Wherever you need the data you can inport context object and use it in componenet.
3)Using Redux library.This is similar to context api but only disadavantage is that we will have to write lot of code to implement this.Redux is a javascript library.
Let me know if you need more info.
You need to :
<>
<Item {props.agent} />
{componentToRender &&
<PopupDialog abc={componentToRender} /> //you must call in this component, in this case i name it is abc , i pass componentToRender state to it
}
</>
and then PopupDialog will receive componentToRender as abc, in PopupDialog , you just need to call props.abc and done .
If you need to know more about prop and component you can see it here
I think what you want to use is Higher-Order-Components (HOC).
The basic usage is:
const EnhancedComponent = higherOrderComponent(WrappedComponent);
Below is such an implementation that takes a component (with all its props) as a parameter:
import React, { Component } from "react";
const Content = WrappedComponent => {
return class Content extends Component {
render() {
return (
<>
{/* Your Content component comes here */}
<WrappedComponent {...this.props} />
</>
);
}
};
};
export default Content;
Here is the link for higher-order-components on React docs: https://reactjs.org/docs/higher-order-components.html
Make use of
useContext()
Follow this for details:
React Use Context Hook

"A valid React element (or null) must be returned." with array.map

I have the Functional React component defined below:
import React from 'react';
import Cell from './FeedCell';
import PropTypes from 'prop-types';
const Feed = (props) => {
return props.arr.map((type, index) => {
return (<Cell />)
})
};
I am getting the error:
Feed(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
I feel like this should be obvious but I have tried the solutions presented online but I am missing something.
You are currently returning an array, but you must return a proper React element.
You can wrap your array in a React.Fragment if you don't want a wrapping element in the DOM.
const Feed = (props) => {
return (
<React.Fragment>
{props.arr.map((type, index) => <Cell />)}
</React.Fragment>
);
};
You need to wrap your array in some element
If you don't want to wrap your FeedCells in an actual element, you can use React.Fragment instead:
const Feed = (props) => {
return (
<React.Fragment>
{ props.arr.map((type, index) => <Cell />) }
</React.Fragment>
);
};
More information here:
https://reactjs.org/docs/fragments.html
Can you add more information? - FeedCell code and the parent of Feed.
Try console logging the props and check if props.disputesToRender actually exists or has value in all your render calls.
Check if FeedCell actually returns html or a component that returns an html.
You should also add a <div> or <React.Fragment> to enclose your return method. Since there should always be a single parent on returns.

Categories

Resources