I'm passing data props to two components which are exactly the same like this :
export default function Home({ data }) {
return (
<Layout title="Home Page">
<CarouselMobile
aig={data.aig}
audi={data.audi}
bt={data.bt}
francetelecom={data.francetelecom}
hkt={data.hkt}
kelly={data.kelly}
mobinnet={data.mobinnet}
orange={data.orange}
pccw={data.pccw}
sap={data.sap}
tata={data.tata}
teleperformance={data.teleperformance}
wf={data.wf}
/>
<CarouselDesktop
aig={data.aig}
audi={data.audi}
bt={data.bt}
francetelecom={data.francetelecom}
hkt={data.hkt}
kelly={data.kelly}
mobinnet={data.mobinnet}
orange={data.orange}
pccw={data.pccw}
sap={data.sap}
tata={data.tata}
teleperformance={data.teleperformance}
wf={data.wf}
/>
</Layout>
);
}
How can I refactor this data code which is being passed to both components so it doesn't look messy like this ?
Just spread it if the names are equal.
<CarouselMobile {...data} />
<CarouselDesktop {...data} />
If not you can also partially spread props with the same name in your data object and the component, and apply other props which have different names.
<CarouselMobile {...data} differentProp={differentProp} />
<CarouselDesktop {...data} anotherDifferentProp={anotherDifferentProp} />
Or based on the comments, you can also pass the entire data object like below.
<CarouselMobile data={data} />
But the point here is that you would access attributes in CarouselMobile with data.aig for instance like below.
const CarouselMobile = ({ data }) => {
const { aig, audi, /* other props */ } = data;
};
Here, you can create a common object with these props and send that object to both components.
Or you can send data prop to the components and use there directly.
Another way, send data prop by using spread operator.
Related
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
}
)
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.
);
})}
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
so i am fetching a data in react, and i want to pass an individual value from the response in a context from a parent as prop, trendingData is from a response in api call. code:
if (loading) return <Loading />;
return (
<VidoesContainer>
<TopBar />
<FitnessChallenge />
<MainContainer>
<Category categoryData={trendingData} />
<Trainers />
</MainContainer>
</VidoesContainer>
);
the initial value of trendingData is undefined, trendingData is passed to a screen stacks in react navigation. I want to use it in context like this:
const CategoryScreenStack = ({ categoryData }) => {
return (
<CategoryContext.Provider value={categoryData}>
<Stack.Navigator initialRouteName='Category'>
<Stack.Screen name='Category' component={Category} />
<Stack.Screen
name='Video'
options={{
transitionSpec: {
open: TransitionSpecs.FadeInFromBottomAndroidSpec,
close: TransitionSpecs.FadeOutToBottomAndroidSpec,
},
}}
component={Video}
/>
</Stack.Navigator>
</CategoryContext.Provider>
);
};
but i get an error: undefined is not an object (evaluating '_useContext.name') when i only just want to avoid prop drilling on each screens. How do i pass a prop from a parent as context to its child?
[Edit] After reading again your question, this doesn't answer it, and it's pretty dumb since you are probably already using hooks in your children component.
Now, it looks like you are trying to access a property name of undefined so make sure your data are available in your context. Here ishow i would handle this:
export const App = () => {
const [trendingData, setTrendingData] = useSate(undefined);
useEffect(() => {
fetch('...').then((response) => {
setTrendingData(response.someData);
});
});
const loading = trendingData === undefined;
if (loading) return <Loading />;
return (
<VidoesContainer>
<TopBar />
<FitnessChallenge />
<MainContainer>
<Category categoryData={trendingData} />
<Trainers />
</MainContainer>
</VidoesContainer>
);
};
The idea is to add trendingData in your condition to render or not your component. Here i represent it by just a simple condition, you will have to adapt it to your own way. Same thing goes for the effect, I did a fetch inside but just adapt it or provide more code so i can be more precise.
Hope it helps :)
I think you need to dispatch your trendingData after you API call. This way you will be able to use it as a normal context value. You could make use of the useContext():
[state, dispatch] = useContext(CategoryContext);
This way you will have access to your state. It can be done in every stateless component where you need to use the state or dispatch new data to it. Of course you will need a reducer.
If you don't know what I'm talking about, I highly recommand you to take a look at The official documentation aboutHooks
I have an array of components that I want to render, but I don't know how to send props to each of them. Do you have any idea how? I am using also using formik for form management and the page components consist of basic inputs.
const pages = [<Page1 />, <Page2 />, <Page3 />, <Page4 />]
and I am rendering them this way:
<div>{pages[state]}</div>
You can accomplish this by using React.cloneElement:
const newProps = {}
<div>{React.cloneElement(pages[state], newProps)}</div>
React.cloneElement
There are 2 options to achieve this:
Option 1: On the declaration site you may pass the state while initialising the array.
const pages = [<Page1 props={page1Prop: 'value'}/>,
<Page2 props=page2Props />,
<Page3 props=page3Props />,
<Page4 props=page4Props />]
Or if you do not want or can not update the declaration you may pass when rendering:
Or you can
Option 2: clone the component prop and apply the new props
...
render() {
return (
<div>
{React.cloneElement(pages[currentPage] propsForPage[currentPage])}
OR
{React.cloneElement(pages[currentPage] {someProp:'value'})}
</div>
);
}
}
Could you not initialise the component in the array?
const pages = [Page1, Page2, Page3, Page4]
return (
<div>
{pages.map(page => {
const Component = <page />
return (
<Component {...props} />
)
}
</div>
)