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
Related
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 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
I am learning react by going through a react tutorial and i am getting a map undefind error. What should i do?
I am trying to iterate over the array to display the data of the array in the player component but i am still getting this error. i tried searching online and looking through at other map undefined error on stack overflow but none is like my problem that i am having.
const players = [
{
name: "Guil",
score: 50
},
{
name: "Treasure",
score: 85
},
{
name: "Ashley",
score: 95
},
{
name: "James",
score: 80
}
];
const Player = (props) => {
return (
<div className="player">
<span className="player-name">
{props.name}
</span>
<Counter score={props.score} />
</div>
);
}
const App = (props) => {
return (
<div className="scoreboard">
<Header
title="Scoreboard"
totalPlayers={4}
/>
{/* Players list */}
{props.initialPlayers.map(player =>
<Player
name={props.name}
score={props.score}
/>
)}
</div>
);
}
ReactDOM.render(
<App initialPlayers={ players}/>,
document.getElementById('root')
);
export default App;
Considering you didn't give us the error message, I can't be sure of what is actually undefined, although I doubt that map is undefined.
In the snippet below, you're trying to access props.name and props.score, which don't exist in the context. You've called the player player within the map callback and need to access it as such.
i.e. change props.name and props.score to player.name and player.score.
{props.initialPlayers.map(player =>
<Player
name={props.name} // player.name
score={props.score} // player.score
/>
)}
Looks like cubrr's comment might have identify the issue that you are running into. You are probably getting the error here:
const App = (props) => {
return (
<div className="scoreboard">
<Header
title="Scoreboard"
totalPlayers={4}
/>
{/* Players list */}
{props.initialPlayers.map(player =>
<Player
name={props.name}
score={props.score}
/>
)}
</div>
);
}
since props.name = undefined, you are trying to render something that does not exist. You will need to change it to:
{props.initialPlayers.map(player =>
<Player
name={player.name}
score={player.score}
/>
)}
Hope that helps.
React is relatively good at providing error logs for you, please be sure to take a look at error logs and it will tell you which line the error is occurring on.
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;
I'd like to know what's the best pattern to use in the following use case:
I have a list of items in my ItemList.js
const itemList = items.map((i) => <Item key={i}></Item>);
return (
<div>{itemList}</div>
)
Each of this Items has an 'EDIT' button which should open a dialog in order to edit the item.
Where should I put the Dialog code?
In my ItemList.js => making my Item.js call the props methods to open the dialog (how do let the Dialog know which Item was clicked? Maybe with Redux save the id of the item inside the STORE and fetch it from there?)
In my Item.js => in this way each item would have its own Dialog
p.s. the number of items is limited, assume it's a value between 5 and 15.
You got a plenty of options to choose from:
Using React 16 portals
This option let you render your <Dialog> anywhere you want in DOM, but still as a child in ReactDOM, thus maintaining possibility to control and easily pass props from your <EditableItem> component.
Place <Dialog> anywhere and listen for special app state property, if you use Redux for example you can create it, place actions to change it in <EditableItem> and connect.
Use react context to send actions directly to Dialog, placed on top or wherever.
Personally, i'd choose first option.
You can have your <Dialog/> as separate component inside application's components tree and let it to be displayed in a case if your application's state contains some property that will mean "we need to edit item with such id". Then into your <Item/> you can just have onClick handler that will update this property with own id, it will lead to state update and hence <Dialog/> will be shown.
UPDATED to better answer the question and more completely tackle the problem. Also, followed the suggestion by Pavlo Zhukov in the comment below: instead of using a function that returns functions, use an inline function.
I think the short answer is: The dialog code should be put alongside the list. At least, this is what makes sense to me. It doesn't sound good to put one dialog inside each item.
If you want to have a single Dialog component, you can do something like:
import React, { useState } from "react";
import "./styles.css";
const items = [
{ _id: "1", text: "first item" },
{ _id: "2", text: "second item" },
{ _id: "3", text: "third item" },
{ _id: "4", text: "fourth item" }
];
const Item = ({ data, onEdit, key }) => {
return (
<div key={key}>
{" "}
{data._id}. {data.text}{" "}
<button type="button" onClick={onEdit}>
edit
</button>
</div>
);
};
const Dialog = ({ open, item, onClose }) => {
return (
<div>
<div> Dialog state: {open ? "opened" : "closed"} </div>
<div> Dialog item: {JSON.stringify(item)} </div>
{open && (
<button type="button" onClick={onClose}>
Close dialog
</button>
)}
</div>
);
};
export default function App() {
const [isDialogOpen, setDialogOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const openEditDialog = (item) => {
setSelectedItem(item);
setDialogOpen(true);
};
const closeEditDialog = () => {
setDialogOpen(false);
setSelectedItem(null);
};
const itemList = items.map((i) => (
<Item key={i._id} onEdit={() => openEditDialog(i)} data={i} />
));
return (
<>
{itemList}
<br />
<br />
<Dialog
open={isDialogOpen}
item={selectedItem}
onClose={closeEditDialog}
/>
</>
);
}
(or check it directly on this CodeSandbox)