React component with two sets of children - javascript

I'm creating a component that needs to take in two sets of children and to be placed in two different parts of a component.
let CreditCardForm = ({icons, fields}) => (
<div>
<div className='some'>
<div className='special'>
<div className='nesting'>
{icons}
</div>
</div>
</div>
{fields}
</div>
)
let CreditCardFormUsage = () => {
let icons = () => (
<Icons>
<IconBogus/>
<IconVisa/>
<IconPaypal/>
<IconMore/>
</Icons>
)
let fields = () => (
<CreditCardFields>
<FieldCardNumber/>
<FieldName/>
<FieldExpirey/>
<FieldCCV/>
</CreditCardFields>
)
return (
<CreditCardForm icons={icons} fields={fields}/>
)
}
The code above should work, my question is it possible to grab those property values based on the children in the element itself, and have something more natural?
<CreditCardForm>
<Icons>
<IconBogus/>
<IconVisa/>
<IconPaypal/>
<IconMore/>
</Icons>
<CreditCardFields>
<FieldCardNumber/>
<FieldName/>
<FieldExpirey/>
<FieldCCV/>
</CreditCardFields>
</CreditCardForm>

Yes, this.props.children will return an array so if you always want to load specific children, then just reference those children by index in your wrapper. Then you could just turn icons and fields into wrapper components. Here is a working jsfiddle. See how the render method of App is exactly what you want.
CreditCardForm render:
<div>
<div className='some'>
<div className='special'>
<div className='nesting'>
{this.props.children[0]}
</div>
</div>
</div>
{this.props.children[1]}
</div>
Fields and Icons render:
<div>{this.props.children}</div>
App render:
<CreditCardForm>
<Icons>
<IconBogus />
</Icons>
<Fields>
<FieldCardNumber />
<FieldName />
</Fields>
</CreditCardForm>

yes, you can do it with child props. Read more #docs:
https://facebook.github.io/react/tips/children-props-type.html
And of course check out React.Children
https://facebook.github.io/react/docs/top-level-api.html#react.children

Related

Each child in a list should have a unique "key" prop Reactjs

I am working on Nextjs/Reactjs i am fetching data after click on button but in my console i am getting following error
Each child in a list should have a unique "key" prop
Here is my current code ( data is displaying with id and title)(
<button onClick={fetchReviewsFromServer}>Load Reviews From Server</button>
{
reviewfromserver.map(reviewnew=>{
return(
<>
<div key={reviewnew.id}> // not working
<div>{reviewnew.id}- {reviewnew.title} </div>
</div>
</>
)
})
}
The child in your list is the empty <> element, I don't see a need for it regardless, since you only have one child element to that element.
<button onClick={fetchReviewsFromServer}>Load Reviews From Server</button>
{
reviewfromserver.map(reviewnew=>{
return(
<div key={reviewnew.id}> // not working
<div>{reviewnew.id}- {reviewnew.title} </div>
</div>
)
})
}
That's because the fragmets are items. Remove them, you don't need them anyway.

Access Individual Elements in an Array from an API (react)

I am a beginner and I am trying to create a recipe app. I managed to set up an API that gives an Array of 10 objects each time I search for a meal like so.
I access the elements of each recipe using a map
{recipes.map(recipe =>(
<RecipeCard
key={recipe.recipe.label}
title ={recipe.recipe.label}
calories={recipe.recipe.calories}
image={recipe.recipe.image}
ingredients={recipe.recipe.ingredients}
/>
))}
Here is also my Const Recipe Card just for some more context. It functions fine.
const RecipeCard = ({title, calories, image, ingredients}) => {
const round = Math.round(calories);
return(
<div className = {style.recipe}>
<h1 >{title}</h1>
<ol className = {style.list}>
{ingredients.map(ingredient => (
<li>{ingredient.text}</li>
))}
</ol>
<p>calories: {round} </p>
<img className = {style.image} src={image} alt=""/>
<button>Add to Favorites</button>
</div>
)
}
I currently only want to access the information from the first array, but whenever I change recipes.map to recipes[0] it says that function does not exist. How should I go about accessing individual elements from the arrays provided from the API?
You can use .slice(0, 1) to create a shallow copy (a new array with just first element):
{recipes.slice(0, 1).map(recipe =>(
<RecipeCard
key={recipe.recipe.label}
title ={recipe.recipe.label}
calories={recipe.recipe.calories}
image={recipe.recipe.image}
ingredients={recipe.recipe.ingredients}
/>
))}
Or use destructuring:
const [recipe] = recipes // before "return"
// ....
<RecipeCard
key={recipe.recipe.label}
title ={recipe.recipe.label}
calories={recipe.recipe.calories}
image={recipe.recipe.image}
ingredients={recipe.recipe.ingredients}
/>
Or use index:
<RecipeCard
key={recipes[0]?.recipe.label}
title ={recipes[0]?.recipe.label}
calories={recipes[0]?.recipe.calories}
image={recipes[0]?.recipe.image}
ingredients={recipes[0]?.recipe.ingredients}
/>
The ?. is called optional chaining, you can use it to avoid error like Can't Read property of undefined, i.e. when the first element is undefined and you try to read its properties.

How to manipulate child's render output

I'm using a component form a third party library and I want to manipulate the render output.
Let's say we have two components:
// third party component that I don't have access to its code
const ThirdPartyComponent = () => (
<div>
<span>
...
</span>
</div>
)
// My component using the third party component
const MyComponent = () => (
<div className="wrapper">
<ThirdPartyComponent />
</div>
)
Normally the output will be:
<div class="wrapper">
<div>
<span>
...
</span>
</div>
</div>
Now I want to manipulate the third party component such that my component renders this:
<div class="wrapper">
<span>
...
</span>
</div>
Or in other words unwrap the rendered content from an unwanted <div /> element.
1. I can't change the third party code
2. The child component doesn't forward ref
Any idea how to do this? 🤔
If:
the third party component is a function component, and
the third party component does not use refs (or if you're careful), and
you don't mind losing the component boundary for ThirdPartyComponent (essentially MyComponent will "be" ThirdPartyComponent), and
you solemnly swear you understand this might break in the future
you can call the component as if it was a function, then dig into the returned JSX object and get the first child, á la:
const ThirdPartyComponent = () => (
<div style={{border: "1px solid orange"}}>
<span>...</span>
</div>
);
const MyComponent = () => {
const thirdPartyDiv = ThirdPartyComponent(); // This is naughty!
const span = thirdPartyDiv.props.children; // The child is the span
return (
<div className="wrapper">
{span}
</div>
);
}
Alternately, if all you're doing is re-wrap the component, you could modify thirdPartyDiv.props.className to suit your needs...
const MyComponent = () => {
const thirdPartyDiv = ThirdPartyComponent(); // This is naughty!
thirdPartyDiv.props.className = "wrapper";
return thirdPartyDiv;
}

Child state updated but not rendering [duplicate]

This question already has answers here:
map is not rendering the ui elements in reactjs
(2 answers)
Closed 2 years ago.
I have a child component named Recently Opened. There are no props passed to the child component from parent component. The rendor method of parent component is:
render() {
return (
<div>
<RecentlyOpened />
</div>
);
}
The render method of child component(RecentlyOpened) looks like this:
render() {
return (
<div className='outer-box'>
{this.state.items.map(item => {
<div className='item-box'>
<img className='image' src={'/src/assets/' + item.imgURL} />
<div className='name item-margin'>{item.name}</div>
<div className='id item-margin'><b>SKU/ID: </b>{item.productId}</div>
</div>;
})}
</div >
);
}
Here this.state.items is populated from an API call. But even as the state is updated(I have checked this) after the API call returns the data, the UI inside the map function doesn't get displayed and just the empty div is rendered. Shouldn't the UI be updated once the state of child component changes?
You are not returning a value in your map loop. A simple fix is to replace the curly brackets with parentheses.
render() {
return (
<div className='outer-box'>
{this.state.items.map(item => (
<div className='item-box'>
<img className='image' src={'/src/assets/' + item.imgURL} />
<div className='name item-margin'>{item.name}</div>
<div className='id item-margin'><b>SKU/ID: </b>{item.productId}</div>
</div>;
))}
</div >
);
}

Why props alone are being used in called React function component?

I was learning React and I came to a point which created confusion. Everywhere I was using props while writing Function components.
I always use props.profile and it works fine. But in one code component, I had to write
const profiles=props; and it worked fine.
I tried using const profiles=props.profile; and also I tried using inside return in 'Card' function component
{props.profile.avatar_url} but both of them failed
Below is my code which works fine
const Card=(props)=>{
const profiles=props; //This I dont understand
return(
<div>
<div>
<img src={profiles.avatar_url} width="75px" alt="profile pic"/>
</div>
<div>
<div>{profiles.name}</div>
<div>{profiles.company}</div>
</div>
</div>
);
}
const CardList=(props)=>{
return(
<div>
{testDataArr.map(profile=><Card {...profile}/>)}
</div>
);
}
Can someone please help me understand why I can't use const profiles=props.profile?
What are the other ways to achieve the correct result?
Your testDataArr might be this,
testDataArr = [{avatar_url:"",name:"",company:""},{avatar_url:"",name:"",company:""},{avatar_url:"",name:"",company:""}]
Now when you do this,
{testDataArr.map(profile=><Card {...profile}/>)}
here profile = {avatar_url:"",name:"",company:""},
and when you do,
<Card {...profile}/>
is equivalent to,
<Card avatar_url="" name="" company=""/>
In child component, when you do this,
const profiles=props;
here props = {avatar_url:"",name:"",company:""}
So you can access it's values,
props.avatar_url
props.name
props.company
But when you do this,
const profiles=props.profile
profile key is not present in {avatar_url:"",name:"",company:""} object and it fails.
OK. Here is the issue, the props object does not contain a profile attribute, but IT IS the profile attribute. Becouse you are spreading the profile variable when you render the Card element (in the CardList), you basically are writing:
<Card avatarUrl={profile.avatarUrl} comapny={profile.comany} />
Instead, you should do
<Card profile={profile} />
and then in your Card component access the data this way
const Card = (props) => {
const profile = props.profile
}
or even simpler
const Card = ({profile}) => {
return <div>{profile.comany}</div>
}

Categories

Resources