I have created object with arrays inside it, text and images, and I wish to map through this to render new component, is it possible to map through images like that? Because my new component doesn't detect any picture when I pass it through props and don't know if I do something wrong or it is just not possible that way.
import firstProjectPhoto from '../img/picOne.png';
import secondProjectPhoto from '../img/picTwo.png';
import thirdProjectPhoto from '../img/picThree.png';
import ListOfProjects from "./subcomponents/ListOfProjects";
const projects = [{
photo:[{firstProjectPhoto},{secondProjectPhoto},{thirdProjectPhoto}],
},
{
text:["Project number one", "Project number two", "Project number 3"],
}];
class Projects extends Component {
render() {
return (
<Grid className="text-center" id="listOfProjects">
<PageHeader className="projects-header">My Projects</PageHeader>
{projects.map(e =>
<ListOfProjects
photo={e.photo}
text={e.text}
/>
)}
</Grid>
);
}
}
export default Projects;
new Component
class ListOfProjects extends Component {
render() {
return (
<Row className="show-grid text-center projects-list">
<Col xs={1} sm={1} className="projects">
<Image src={this.props.photo} thumbnail responsive/>
<p>{this.props.text}</p>
</Col>
</Row>
);
}
}
export default ListOfProjects;
UPDATED:
Actually there is some progress, thank you guys:) the problem was in structure of const project, however still it shows img src as a [object Object] instead of normal picture
const projects = [{
photo: {firstProjectPhoto},
text:"first project"
},
{
photo: {secondProjectPhoto},
text:"second project"
},
{
photo: {thirdProjectPhoto},
text:"third project"
}
]
<Grid className="text-center" id="listOfProjects">
<PageHeader className="projects-header">
My Projects </PageHeader>
{projects.map((e,i) =>
<ListOfProjects
photo={e.photo}
text={e.text}
key={i}
/>
)}
</Grid>
UPDATED
I should be without {}
Now works perfectly fine:) thank you everyone for help
The structure of the projects object is not the one you expect. You want
projects = [
{
photo: projectPhoto,
text: projectText
},
...
]
but you have
projects = [
{ photo: [...] },
{ text: [...] }
]
You also forgot to add a key to each item rendered in the loop:
{projects.map((e, idx) =>
<ListOfProjects
photo={e.photo}
text={e.text}
key={idx} // <-- here
/>
)}
Assume that you want to pass a dummy db code, should do it as follows, even if it is not ideal but for testing purpose:
class Projects extends Component {
const projects = [
{
photo:firstProjectPhoto,
text: "Project number one"
},
{
photo:secondProjectPhoto,
text: "Project number two"
},
{
photo:thirdProjectPhoto,
text: "Project number three"
},
];
render() {
return (
<Grid className="text-center" id="listOfProjects">
<PageHeader className="projects-header">My Projects</PageHeader>
{projects.map(e =>
<ListOfProjects
projects={projects}
/>
)}
</Grid>
);
}
}
The problem is the data structure of your projects. Its defined as an array of objects, where key is in array. Try to adjust your project structure.
const projects = [{
photo:'firstProjectPhoto':text:'Project number one'
},
{photo:'secondProjectPhoto':text:'Project number two'},
{photo:'thirdProjectPhoto',:text:'Project number three'}
];
Considering your JSON data structure following is one way to resolve your issue. But the json data you provided is not recommented. Every image should have its name in same object.
class ListOfProjects extends Component {
render() {
let images = [];
const { photo, text } = this.props;
photo.map((p, i) => {
images.push(<Col xs={1} sm={1} className="projects">);
images.push(<div key={i}>
<Image src={p} thumbnail responsive/>
<p>{text[i]}</p></div>
);
images.push(</Col>);
});
return (
<Row className="show-grid text-center projects-list">
{images}
</Row>
);
}
}
export default ListOfProjects;
Related
I want to practice unit test so I created a book store with a variety of components. So I have a relatedBooks component for a react native app that is in a bookDetailsRelatedBooks component. In that component, I have this code below I want to write a unit test to cover.
{relatedBooks.map((relatedBook, index) => (
<React.Fragment key={relatedBook.bookId}>
<Grid item>
<ViewabilityTrackingView
enabled
offsetBottom={150}
accessibilityState={{disabled: true}}
onViewabilityChange={({ isInView }: OnViewabilityCallbackParameters) => {
if (isInView) {
trackRelatedBookImpression(relatedBook.bookId, relatedBook.title, originBookId, index);
}
}}
>
<RelatedBookRow
book={relatedBook}
onClick={() => {
trackRelatedBookClick(relatedBook.bookId, originBookId, index);
}}
/>
</ViewabilityTrackingView>
</Grid>
<Grid item cx={styles.divider}>
<Divider />
</Grid>
</React.Fragment>
))}
I tried this below but it does not work:
import { act, render, fireEvent } from 'react-testing-library';
test('trackRelatedBookImpression is called with correct arguments', () => {
const relatedBooks = [{ bookId: 1, title: 'Test Book 1' }];
const originBookId = 2;
const trackRelatedBookImpression = jest.fn();
const { getByTestId } = render(
<bookDetailsRelatedBooks relatedBooks={relatedBooks} originBookId={originBookId} trackRelatedBookImpression={trackRelatedBookImpression} />
);
const viewabilityTrackingView = getByTestId('viewability-tracking-view');
act(() => {
fireEvent.scroll(viewabilityTrackingView);
});
expect(trackRelatedBookImpression).toHaveBeenCalledWith(1, 'Test Book 1', 2, 0);
});
I expected it to work but I got an error stating:
Found multiple elements with testID: viewabilityTrackingView
I tried assigning a testId to the viewabilityTrackingView but that does not work either, so I'm a bit stuck.
I am working in Reactjs and i am using Nextjs framework, Right now i am tyring to fetch data from database using nextjs, But right now i am getting following error
TypeError: Cannot read property 'id' of undefined,How can i remove this ? Here is my current code
import { Box, Heading } from "#chakra-ui/react";
export async function getStaticProps() {
const response = await fetch("https://fakestoreapi.com/products");
const data = await response.json();
return {
props: {
products,
},
};
}
function Test({products}) {
return (
<Box>
{products.map((product) => (
<Box>
<Text> {product.title} </Text>
</Box>
))}
</Box>
);
}
export default Test;
Here is my index.js file
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Test from '../components/testing/test'
export default function Home() {
return (
<div className={styles.container}>
<Test/>
</div>
)
}
look i think i know where the problem is :
the first problem is that you are using the getStaticProps function in a components while it can only be used in a page (the files inside the pages/ folder) so we need first to move it to index.js like this
index.js
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Test from '../components/testing/test'
export async function getStaticProps() {
const response = await fetch("https://fakestoreapi.com/products");
const products= await response.json(); //<- i changed this becaus it was wrong
return {
props: {
products,
},
};
}
export default function Home({products}) {
return (
<div className={styles.container}>
<Test products={products}/>
</div>
)
}
test.js
import { Box, Heading } from "#chakra-ui/react";
function Test({products}) {
return (
<Box>
{products.map((product) => (
<Box key={product.id}>
<Text> {product.title} </Text>
</Box>
))}
</Box>
);
}
export default Test;
the code above worked for me as it is 'except that my link is different of course'
the second problem is that you were getting your data in the data variable
const data = await response.json();
while returning products variable which is undefined
return {
props: {
products,
},
};
i changed it in your code so it became
const products= await response.json(); //<- i changed this becaus it was wrong
return {
props: {
products,
},
now that should work (it worked in my local envirements)
Notes
i added a key in your map function
<Box>
{products.map((product) => (
<Box key={product.id}>
<Text> {product.title} </Text>
</Box>
))}
</Box>
so it don't give you a warning but thats only possible if your product have an id property so if it gave you an error about id property just remove it.
second notes is that my products is structured like this
[
{
"id": "12346",
"title": " test"
},
{
"id": "154346",
"title": " just"
},
{
"id": "169346",
"title": " another"
},
{
"id": "154326",
"title": " example"
}
]
so if your structur is different it may cause problems
first of all you should pass key value in map function like key={products.id},
and in the next step check part of code
return {
props: {
products,
},
};
do you want to pass products as props or data as props?
and check whether API link https://fakestoreapi.com/products is correct?
in the last step, check response in console.log().
I'm using react-player component for this.
I have file 1 where I'm storing some data as an array, including a local video.
import videoSample from "../assets/videos/sample-video.mov";
export const Data = {
ProjectList: [
{
title: "My title",
desc: "Some description",
videoUrl: { videoSample },
sourceUrl: "https:// ... ",
},
],
};
File 2 takes the array and maps each item to a React component called ProjectDetail.
function MappingFunction() {
return (
<>
{Data.ProjectList.map((project, index) => (
<ProjectDetail key={index} {...project} />
))}
</>
);
}
Finally, this is file 3 which contains ProjectDetail. It takes the array item as props. videoUrl is passed to the ReactPlayer component.
export default function ProjectDetail(props) {
return (
<>
<div>
<ReactPlayer
url={props.videoUrl} // does not work!
width="500px"
/>
</div>
<div>
<h2>{props.title}</h2> // works
<p>{props.desc}</p> // works
<button
onClick={() => { window.open(props.sourceUrl, "_blank"); }} // works
> Click to see more
</button>
</div>
</>
);
}
title, desc and sourceUrl are working fine, but I don't understand videoUrl doesn't. I tried looking up an answer but was unsuccessful.
If I import videoSample in file 3 directly, it works fine, but not when passed as a prop from outside. What am I missing?
Found my mistake. All I needed to do was removing the curly brackets.
videoUrl: { videoSample } -> videoUrl: videoSample
I'm trying to build a component who going to have multiple objects from the array.
I want to show them one by one.
Let's assume, I have 3 objects inside the array. Then first object-related content should be shown first until the user opts to continue or skip for the next object until all 3 objects have been shown. How can I do this inside the One page?
For example, this is a minimal Code that how I'm going to make a component, I want to handle the Data like that each object should be shown independently and move to next on user input. Whether the user wants to skip or continue, the next object should be shown on the same page.
import { Fragment } from 'react'
import Data from 'Data'
const Main = () => {
const data = [
{ id: 1, more: '...' },
{ id: 2, more: '...' },
{ id: 3, more: '...' }
]
const submitHandler = () => {
// some action
}
return (
<Fragment>
<Card style={{ minHeight: '40rem' }}>
<Card.Body>{data ? data.map((el) => <div key={el.id} >
<Data obj={el} /> // Passing to child
</div>) : null}
</Card.Body>
<Card.Footer>
<Button variant="outline-danger" onClick={submitHandler} className="mx-1">
Skip
</Button>
<Button variant="primary" onClick={submitHandler}>
Continue
</Button>
</Card.Footer>
</Card>
</Fragment>
)
}
export default Main
Edit:
#jasonmzx below suggested some solution but it's giving type error. Can anybody fix this here , CodeSandBox
Here you could use the state to track which index the user is currently on and render the output based on your array
import { Fragment, useState } from 'react';
import Data from 'Data';
const Main = () => {
const [index,setIndex] = React.useState(0); // Starting from the beginning
const data = [
{ id: 1, more: '...' },
{ id: 2, more: '...' },
{ id: 3, more: '...' }
]
// I'm making this a function for cleanliness
// This renders the array at the index of the state
const showDat = (data, index) => {
return
<p>{data[index].more}</p>
}
const submitHandler = () => {
// Skip or cont: update state!
setIndex(index+1);
}
return (
<Fragment>
<Card style={{ minHeight: '40rem' }}>
<Card.Body>{data ? data.map((el) => <div key={el.id} >
<Data obj={el} /> // Passing to child
</div>) : null}
</Card.Body>
<Card.Footer>
<Button variant="outline-danger" onClick={submitHandler} className="mx-1">
Skip
</Button>
<Button variant="primary" onClick={submitHandler}>
Continue
</Button>
{showDat(data, index)}
</Card.Footer>
</Card>
</Fragment>
)
}
export default Main
Basically here is what will happen (when you code the submitHandler); the submitHandler updates the state, the state adds 1 onto index, and since the state has updated, a re-render happens, meaning the showDat() function being called within the Main component's render is being called again with the new index, to display the next index in ur array
While I wrote my component as much DRY as I could, I wonder if there is a better way to render ConnectButton through a map function that takes [linkedIn, twitter, researchGate] as an array. I tried different things, but that's as good as I could get it. (I simplified the code a bit to the relevant. The original version is by a lot longer.)
const Component = () => {
const ConnectButton = ({ icon, title }) => (
<MenuItem>
<ListItemIcon className={classes.listItemIcon}>
<Icon className={classes.connectIcon} icon={icon} />
</ListItemIcon>
<ListItemText primary={title} />
</MenuItem>
);
const renderConnectMenu = () => {
if (!profileInfo) {
return;
}
const linkedIn = profileInfo.linkedIn;
const twitter = profileInfo.twitter;
const researchGate = profileInfo.researchGate;
return (
<div>
{linkedIn && <ConnectButton icon={faLinkedin} title="LinkedIn" />}
{twitter && <ConnectButton icon={faTwitter} title="Twitter" />}
{researchGate && (
<ConnectButton icon={faResearchgate} title="Research Gate" />
)}
</div>
);
};
render(
<>
[...]
{renderConnectMenu()}
[...]
</>
);
};
There are 3 properties that define the rendering of a button: a field from profileInfo, icon and title. So we will need an array of objects with these properties in order to render them with .map.
I am not sure what kind of date the profileInfo fields hold so I named the property as shouldRender based on its usage.
const renderConnectMenu = () => {
if (!profileInfo) {
return;
}
const buttons = [
{
shouldRender: profileInfo.linkedIn,
icon: faLinkedin,
title: 'LinkedIn',
},
{
shouldRender: profileInfo.twitter,
icon: faLinkedin,
title: 'Twitter',
},
{
shouldRender: profileInfo.researchGate,
icon: faLinkedin,
title: 'Research Gate',
},
];
return (
<div>
{buttons.map(
(button) =>
button.shouldRender && (
<ConnectButton
key={button.title}
icon={button.icon}
title={button.title}
/>
),
)}
</div>
);
};
It might look a bit weird when you have a few items to render. So I wouldn't recommend this approach if there are just 2-3 items unless there is a possibility of extending the list. It's okay to keep it as it is now.