React: Child Components Failed to Receive Multiple Mapped Data Using Props - javascript

I'm new to react. I want to create a simple react web app that receives data from 2 api, and display the data.
I have three components. App.js receive data from two api, and pass the data to FruitGrid.js using hooks and props. FruitGrid.js map the two data received, and pass to FruitItem.
My problem is in FruitGrid.js. Data is received in FruitGrid.js, I can see by console log or print using html tags. But when I try to send the mapped data to FruitItem.js by changing the h1 tag to <FruitItem></FruitItem>, I can only successfully pass one data, not both of them.
FruitGrid.js
import React from 'react';
import FruitItem from './FruitItem';
const FruitGrid = ({ items, images, isLoading }) => {
return isLoading ? (
<h1>Loading...</h1>
) : (
<section>
{items.map((item) => {
return <FruitItem key={item.id} item={item}></FruitItem>
})}
{images.map(image => {
return <FruitItem key={image.id} image={image}></FruitItem>
// return <h1>{image}</h1>
})}
</section>
)
}
export default FruitGrid;
If I only do return <FruitItem key={item.id} item={item}></FruitItem> the item data will show in the correct layout, image won't show since I didn't pass. But if I try to pass both item and image using <FruitItem></FruitItem>. It will show error saying "TypeError: Cannot read property 'name' of undefined" in FruitItem.
FruitItem.js
import React from 'react'
const FruitItem = ({ item, image }) => {
return (
<div className='card'>
<div className='card-inner'>
<div className='card-front'>
<img src={image} alt='' />
</div>
<div className='card-back'>
<h1>{item.name}</h1>
<ul>
</ul>
</div>
</div>
</div>
)
}
export default FruitItem
Can someone help and let me know how i can fix it?

Hey #yywhocodes this is happening because the FruitItem.js is always expecting an item object and your code for the images mapping is not providing the item object -> return <FruitItem key={image.id} image={image}></FruitItem>
What you can do is change the FruitItem.js
from
<h1>{item.name}</h1>
to
{ item ? <h1>{item.name}</h1> : null }
like that it will try to render the item.name only if the item exists.

You are iterating over the two data sources separately, so items are not defined in your images.map and images are not defined in your items.map
In order to combine, assuming the order of the data is the same/the keys are the same in both cases, you could do something like this:
{items.map((item, key) => {
return <FruitItem key={item.id} item={item} image={images[key]}></FruitItem>
})}

You need to pass props key={item.id} item={item} to the child FruitItem component. If you can't pass any props to the FruitItem component, react won't figure out the item.name. Which will be a TypeError.

It's because your FruitItem always expects an item to be handed in
<h1>{item.name}</h1>
But when you hand in your image, there is no item you set.

Related

Can I store a component in a variable and push children to it after it has been created?

Let's say that I have this component:
const Test = ({ children, ...rest }) => {
return <>{children}</>
};
export default Test;
I am wondering if it is possible to create a variable that holds the component like this:
const test = <Test></Test>;
And then loop over some data and push children to the test variable on every iteration.
if you don't have the data yet, then all you have to do is conditionally render your component when you do have the data.
{ data ? (<Test>{data.map(...)}</Test>) : <SomeOtherComponent /> /* or null */}
or
{ data ? <>{data.map((x) => <Test>{x}</Test>)}</> : <SomeOtherComponent /> /* or null */}
depending on what you want achieve, i didn't fully understand your question
i.e. if you have the data you need, render the component, rendering the children as you see fit, otherwise render some other component (or null, to render nothing)
Yeap, try that pattern:
const test = (children) => <Test>{children}</Test>;
and usage
<>
{[1,2,3].map(el=>test(el))}
</>
[Edited]
const TestComp = ({children}) => <Test>{children}</Test>;
<>
{[1,2,3].map(el=>(<TestComp>{el}</TestComp>))}
</>

How to conditional render inside map in a functional component of react js

I have an array. in which i want to iterate over and do conditional checks and display the correct element. i'm using functional component.
Below is an example :
import React, { useState, useEffect } from "react";
function Job(props) {
const [resp_data, setdata] = useState("");
const [look, setlook] = useState("");
useEffect(() => {
// some api calls happen here and response data is stored in state resp_data
}, [props]);
return (
<>
<div className="product_list">
{resp_data.length > 0
? resp_data.map((item) => {
{item.looking_for === "work" ? (
<div className="card-rows work" key={item.id}>
work
</div>
) : (<div className="card-rows no_work" key={item.id}>
No work
</div>)
}
})
: <div>array length is 0</div>}
</div>
</>
);
}
export default Job;
The response data received from api call happening inside useEffect is stored in state variable resp_data.
if resp_data length is not zero, i need to iterate over the list and check if the field value "looking_for" is equal to "work"
if true display a component , else display another component.
if resp_data length is zero display "array length is zero"
this i want to implement, but i could not find many answers for functional components. i have tried many possible ways by switching, changing the braces etc..my hard luck.
Any help is greatly appreciated.
You are not returning anything from the 'map' function.
resp_data.map((item) => (
item.looking_for === "work" ? (
<div className="card-rows work" key={item.id}>
work
</div>
) : (<div className="card-rows no_work" key={item.id}>
No work
</div>)
)
)

How to fix TypeScript function type error?

I have TypeScript code:
const ItemsList: React.FC<any> = async () => {
const data = await Http.get(url);
return (
<ul>
{data.map(item => {
return (
<ItemPreview key={item.id} item={item} />
);
})}
</ul>
)
}
export default ItemsList
And got error:
Type '()=>Promise<JSX.Element>' is not assingable to type 'FC'. Type 'Promise' is missing the folowing properties from type 'ReactElement<any, any>': type, props, key
Have no idea how to fix it. What type should it be instead of 'React.FC'?
You're effectively thinking about it the wrong way. Instead of trying to make React component rendering wait for your data, instead you should be thinking of it in terms of updating state in React. Your component should just render whatever the current state is:
return (
<ul>
{data.map(item => {
return (
<ItemPreview key={item.id} item={item} />
);
})}
</ul>
)
Now, what is data? Clearly it's an array, and you also have an asynchronous operation which gets the values for that array. So currently there are a couple potential states:
An initial empty array
The updated array with data
You'd use React state to track that. For example:
const [data, setData] = useState([]);
Http.get(url).then(result => {
setData(result);
});
This first sets data to an empty array, and then in the response from the asynchronous operation updates it to the new data. Since it's using setState, this will instruct React to update the rendering of the component because state has changed.
So in this case the UI will first show no results, then momentarily will show the data. You can add complexity to this by tracking an "is loading" state of some kind and showing a "loading spinner" or some indication to the user that data is being fetched.

How to pass a prop to ReactNode Object being rendered using map?

function fun(){
const [contentArray,setContentArray] = useState([]);
setContentArray([...contentArray, <A />]);
setContentArray([...contentArray, <A />]);
setContentArray([...contentArray, <A />]);
return(
<div>
{
contentArray.map( (component:React.ReactNode,i:number) => {
return(
component
)}
}
</div>
})
);
}
above code works fine as long as i dont want to pass any prop to these array elements of "contentArray" array.but i want to pass indexes as props to these array elements
code sandbox: https://codesandbox.io/s/quirky-cray-u4jr1?from-embed
while setting the state it should be done as below
setState(prevState => [...prevState, A]
and while rendering it it should b rendered as below
{state.map((Comp, i) => {
return <Comp key={i} index={i} />;
})}
Note: no need to specify type as React.ReactNode, and you need to lookout for not setting state inside render, react doesn't allow it, in order to avoid infinite loop.
Thanks for showing way to do it #bpas247
https://codesandbox.io/s/broken-flower-z87mt

React props.children is not array

According to the react docs, if a component has multiple children, this.props.children should be an array.
I have the following component:
export class Two extends React.Component {
componentDidMount() {
console.log(Array.isArray(this.props.children)); // false
}
render() {
return(
<div>
{this.props.children}
</div>
);
}
};
Which I pass children to in another component's render() method:
<Two>
<Img src="/photos/tomato.jpg"/>
<Img src="/photos/tomato.jpg"/>
</Two>
Why is this.props.children not an array? More importantly, how can I get it to be one?
Found a better solution to this after some digging in the React.Children source. It looks like a .toArray() method has been added in React 0.14, soon to be released.
Once it is out we will be able to simply do something like this:
let children = React.Children.toArray(this.props.children);
It's documented in https://reactjs.org/docs/react-api.html#reactchildren
I found this solution. It will render all children, one or more.
const BigMama = ({ children, styles, className }) => {
return (
<div
styles={{styles}}
className={(className ? className : '')}
>
{
React.Children.map(children, (child) =>
<React.Fragment>{child}</React.Fragment>)
}
</div>)
}
<BigMama
styles={{border: 'solid groove'}}
className='bass-player'
>
<h1>Foo</h1>
<h2>Bar</h2>
<h3>Baz</h3>
<h4>Impossibru!</h4>
<BigMama>
If you want to do something with the results (eg. render the array in a list), this is how you can use children. The React docs talk about how the map function works with children, as it's an opaque structure.
export const List = ({ children }) => {
return (
<ul>
{React.Children.map(children, child => <li>{child}</li>)}
</ul>
)
}
So then you can call
<List>
{item.name}
{item.price}
</List>
There are many ways and some are mentioned above already but if you wanna keep it simple and want to achieve this without any utility then below should work
const content = [
<Img src="/photos/tomato.jpg"/>,
<Img src="/photos/tomato.jpg"/>
];
<Two>
{content}
</Two>
You might find the spread syntax useful in this case. Have you tried it out?
<div>
{ [...this.props.children] }
</div>
Combine with map to manipulate the output.
<div>
{ [...this.props.children].map(obj => <div style="someStyling"> {obj} </div> ) }
</div>
Is it because is it a DOM node? Try console.log(this.props.children) you will notice that it logged a array of objects (note that each object contains information of the child element of the component). I read that node and array are not the same tho they have a same format. Visit Javascript DOMNode List for more information.

Categories