I have a React component that maps data from a .js file, containing objects in array. How can I link my title and images from within this function to access respective pages.
The title of an array of objects is 'Practices' which should open a new practices page when clicked. Within this object, there are a number of items, each of which contain an image of a practice, which when clicked must go to that specific practice page.
Here is sample code from my Practice_Data File..
const PRACTICES_DATA = [
{
id: 1,
title: 'Practices',
routeName: 'practices',
items: [
{
id: 1,
name: 'Defending Centrally | Opposed Skill (12-P5)',
imageUrl: 'https://image.mux.com/D8PU0036BeX3veIIzAKJlqiECDBoEIJak/animated.gif?width=490&height=278&fps=15',
Theme: 12,
},
]
},
export default PRACTICES_DATA;
Here is the code for my practice component:
import React from 'react';
import PRACTICES_DATA from './practices.data';
import CollectionPreview from '../../components/collection-preview/collection-preview';
class PracticesPage extends React.Component {
constructor(props) {
super(props);
this.state = {
collections: PRACTICES_DATA
};
}
render() {
const {collections} = this.state;
return (
<div className='practices-page'>
{collections.map(({ id, ...otherCollectionProps }) => (
<CollectionPreview key={id} {...otherCollectionProps} />
))}
</div>
);
}
}
export default PracticesPage;
There might well be an obvious answer to this, but I have been stuck on this for a while. Any help would be greatly appreciated.
EDIT:
Yes using react router here. The Collection Preview Code is shown below:
import React from 'react';
import CollectionItem from '../collection-item/collection-item.component';
import './collection-preview.styles.scss';
const CollectionPreview =({ title, items }) => (
<div className='collection-preview'>
<h1 className='title'>{title.toUpperCase()}</h1>
<div className='preview'>
{items.filter((item, idx) => idx < 4)
.map(({ id, ...otherItemProps }) => (
<CollectionItem key={id} {...otherItemProps} />
))}
</div>
</div>
);
export default CollectionPreview;
Related
Recently I started a React course where the chapter goal is to create a monster sort of website. Below I will leave the code of the relevant JS and JSX files. My SearchBox input does appear on my screen, however, the monster images with their respective h2 and p are not appearing only in my react localhost. I have tried going through my code to understand why my code is not working and I haven't been able to find a solution. Here I will leave the link to the API where I am obtaining the images from, you just have to change the number before the ? to access the other images. I am aware that classes are a bit outdated due to hooks but the course is focusing on them initially so that we can understand their behavior, so please do not update the code, just help me with the functionality.
https://robohash.org/1?set=set2&size=180x180
App.js File
import React, {Component} from 'react';
import './App.css';
import { CardList } from './components/card-list/card-list';
import {SearchBox} from './components/search-box/search-box';
class App extends Component {
constructor() {
super();
this.state = {
monsters: [],
searchField: ''
};
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(users => this.setState({monsters: users}));
}
render() {
const {monsters, searchField} = this.state;
const filteredMonsters = monsters.filter(monster => monster.name.toLowerCase().includes(searchField.toLowerCase())) //This checks if one of the monsters name includes the text inputted into the search field, and filters out those that do not. We lower case it to avoid capital and lower case issues.
return (
<div className='App'>
<SearchBox
placeholder='Search Monsters'
handleChange={e => this.setState({searchField: e.target.value})}
/>
<CardList monsters={filteredMonsters}/>
</div> //By using filteredMonsters here, initially all will appear but as we place some input, only those that include the searchField will appear
)
}
};
export default App;
card.jsx file
import React from 'react';
export const Card = props => {
return (
<div>
<img src={'https://robohash.org/'+ props.monster.id + '?set=set2&size=180x180'} alt='monster'/>
<h2> {props.monster.name} </h2>
<p> {props.monster.email} </p>
</div>
)
}
card-list.jsx file
import React from 'react';
import { Card} from '../card/card';
import './card-list.css';
export const CardList = props => {
return (
<div className='card-list'>
{props.monsters.map((monster) => {
<Card key={monster.id}/>
})}
</div>
)
};
In CardList, you never even passed a monster object to your Card component. Only a monster id for a key. Basically, you need something like this:
<div className='card-list'>
{props.monsters.map((monster) => {
<Card key={monster.id} monster={monster} />
})}
</div>
Minor suggestion: Use proper destructuring in Card component:
export const Card = ({ monster: { id, name, email } }) => {
return (
<div>
<img src={'https://robohash.org/'+ id + '?set=set2&size=180x180'} alt='monster'/>
<h2> {name} </h2>
<p> {email} </p>
</div>
)
}
Working Sandbox: https://codesandbox.io/s/fervent-cerf-qcubt?file=/src/App.js
I am trying to display images for an art gallery. I have one MainGallery component and within said Component is a Piece Component being rendered for each object in a helper file portfolio.js
The Piece Component is rendering for each object and I have access to the data from objects but the image path isn'y displaying images even though the file paths are correct.
MainGallery.js
import React, { useState } from 'react';
import portfolio from '../../portfolio';
import Piece from '../Piece/Piece';
export default function MainGallery() {
// const [pieces, setPieces] = useState(Object.keys(portfolio).map((x) => x));
return (
<div>
{Object.keys(portfolio).map((key) => (
<Piece
key={key}
index={key} // if we need access to key we need to pass it down as prop as something other than 'key'
details={portfolio[key]}
/>
))}
</div>
);
}
Piece.js
import React from 'react';
export default class Piece extends React.Component {
render() {
// deconstruct properties for Piece
const { title, imgPath, description } = this.props.details;
return (
<div className="single-piece">
{console.log(imgPath)}
<h1>{title}</h1>
<img src={imgPath} alt={title} />
<p>{description}</p>
</div>
);
}
}
portfolio.js
const portfolio = [
{
title: 'Storm',
imgPath: '../../images/storm.jpg',
description: 'An owl done with ',
type: 'misc',
style: 'acrylic',
},
{
title: 'eagle',
imgPath: '../../images/eagle.jpg',
description: 'A bald eagle in the wild',
type: 'pet',
style: 'paint',
},
{
title: 'family',
imgPath: '../../images/family.jpg',
description: 'A portrait of my son and nieces.',
type: 'people',
style: 'pastel',
},
];
export default portfolio;
Try using require in the src path.
<img src={require(imgPath)} alt={title} />
If that doesn't work, create a js file in the images folder and export the images like this:
export const poster1 = require('./storm.jpg');
The third option would be to try to import all the images in your component, but that would require a lot more changes to your code.
import storm from '../../images/storm.jpg'
and the final thing to try would be to make sure you keep all your images in the public folder instead of src. Then access them like so:
<img src='/images/storm.jpg' />
Hope that helps.
I am passing array of object as a prop from App.js -> searchResult -> TrackList.js. But when I apply .map function on the array of object it shows Cannot read property 'map' of undefined . I have tried different ways to solve this but none of them worked. In console I am getting the value of prop. And my TRackList.js component is rendering four times on a single run. Here is the code
App.js
this.state = {
searchResults: [
{
id: 1,
name: "ritik",
artist: "melilow"
},
{
id: 2,
name: "par",
artist: "ron"
},
{
id: 3,
name: "make",
artist: "zay z"
}
]
return ( <SearchResults searchResults={this.state.searchResults} /> )
In Searchresult .js
<TrackList tracked={this.props.searchResults} />
In TrackList.js
import React from "react";
import Track from "./Track";
export default class TrackList extends React.Component {
constructor(props) {
super();
}
render() {
console.log("here", this.props.tracked);
return (
<div>
<div className="TrackList">
{this.props.tracked.map(track => {
return (<Track track={track} key={track.id} />);
})}
</div>
</div>
);
}
}
Here is the full code -- https://codesandbox.io/s/jamming-ygs5n?file=/src/components/TrackList.js:0-431
You were loading the Component TrackList twice. One time with no property passed, that's why it was first set in console then looks like it's unset, but it's just a second log. I have updated your code. Take a look here https://codesandbox.io/s/jamming-ddc6l?file=/src/components/PlayList.js
You need to check this.props.tracked.map is exists before the loop.
Solution Sandbox link:https://codesandbox.io/s/jamming-spf7f?file=/src/components/TrackList.js
import React from "react";
import Track from "./Track";
import PropTypes from 'prop-types';
export default class TrackList extends React.Component {
constructor(props) {
super();
}
render() {
console.log("here", typeof this.props.tracked);
return (
<div>
<div className="TrackList">
{this.props.tracked && this.props.tracked.map(track => {
return <Track track={track} key={track.id} />;
})}
</div>
</div>
);
}
}
TrackList.propTypes = {
tracked: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
artist: PropTypes.string,
}))
};
You need to check this.props.tracked value before implementing the map function.
you can simply check using this.props.tracked && follow your code.
You should add searchResults={this.state.searchResults} in your app.js to Playlist, take it in Playlist with props, and then set it in TrackList from Playlist (tracked={props.searchResults}).
Also, Typescript helps me not to do such mistakes.
Also, add a key prop to your component that you return in the map function.
I have an array with different values in state within my component.
I want to dynamically render different sections of an array to the same component but on different pages of my react app.
i.e Pass props down to Component that renders the first title of the array on one page but then render the second title on another page.
Can anyone help?
import React from 'react';
import ChartItem from './chart-item.component.js'
import Popup from './modal/index.js'
//styles
import './chart-slides.styles.css'
class Chart extends React.Component {
constructor() {
super();
this.state = {
sections: [
{
title: 'How do you compare to the competition?',
id: 'chart1',
iframeUrl: 'https://app.chattermill.io/c/f4a52535-b2b9-4d71-9ac7-e198c9452e3f?compact=true'
},
{
title: 'Main drivers of positive sentiment sentiment are brand, service, and app exeprience. While bugs and security features are big drivers of negative sentiment',
id: 'chart2',
iframeUrl: 'https://app.chattermill.io/c/f4a52535-b2b9-4d71-9ac7-e198c9452e3f?compact=true'
},
{
title: 'Scroll through the feedback to see the pain points for customers',
id: 'chart3',
iframeUrl: 'https://app.chattermill.io/c/f4a52535-b2b9-4d71-9ac7-e198c9452e3f?compact=true'
}
]
};
}
render() {
return (
<div>
{this.state.sections.map(({ id, title, iframeUrl }) => (
<ChartItem key={id} title={title} url={iframeUrl} />
))}
<Popup/>
</div>
);
}
}
export default Chart;
How can I only render the first section from my array in this component below? The component below will be showed in multiple pages within the app.
import React from 'react';
import Popup from '../modal/index.js'
import { Heading } from 'rebass'
import './chart-slides.styles.css'
import Iframe from 'react-iframe'
const ChartItem = ({ title, iframeUrl, id }) => (
<div>
<a className="demo-btn" id="demo-btn-chart1" href="https://app.hubspot.com/meetings/jack123/presentation" target="_blank" rel="noopener noreferrer"> Book Demo</a>
<Heading fontSize="2.8vh" textColor="secondary" className="mobile-chart-title" as='h1'>
{title}
</Heading>
<Iframe
url={iframeUrl}
className="iframe-container"
/>
</div>
);
export default ChartItem
I am trying to output some svgs and output them from a list, here is my render method:
render() {
const renderTag = () => {
const Tag = this.props.id
return(<Tag />)
}
return (
<div key={this.props.name} className="social-box">
<a className={this.props.id + "-link"}>
{renderTag()}
</a>
</div>
)
}
However, the DOM node is always lowercase i.e. <facebook> rather than <Facebook> this.props.id is correctly rendered to the console as Facebook. Can anyone tell me why react or the browser incorrectly renders as lowercase, and therefore not the component, and how to fix?
It's a technical implementation of React, all tags get lowercased on this line here, AFAIK it's not possible to render non-lowercased tags and that is by design.
Read more here.
i suggest that you would take a look at this article about dynamic components.
The most relevant example from the article:
import React, { Component } from 'react';
import FooComponent from './foo-component';
import BarComponent from './bar-component';
class MyComponent extends Component {
components = {
foo: FooComponent,
bar: BarComponent
};
render() {
const TagName = this.components[this.props.tag || 'foo'];
return <TagName />
}
}
export default MyComponent;
you most likely have a limited amount of components that could be rendered, so you might create a dictionary that contain a key (name of the component) to the component itself (as shown in the example) and just use it that way:
import Facebook from './FaceBook';
import Twitter from './Twitter';
const components = {
facebook: Facebook,
twitter: Twitter
};
render() {
return <div key={this.props.name} className="social-box">
<a className={this.props.id + "-link"}>
<components[this.props.id] />
</a>
</div>;
}
I find the answer eventually. #TomMendelson almost had the answer, but it needed fleshing out a bit more.
A function to create the component outside of the render method, suggested by #ShubhamKhatri actually did the job. Here's the final code:
import React from 'react';
import Facebook from './svg/Facebook';
import LinkedIn from './svg/LinkedIn';
import Twitter from './svg/Twitter';
import Pinterest from './svg/Pinterest';
class SocialMediaBox extends React.Component {
renderElement(item) {
const Components = {
'Facebook': Facebook,
'Twitter': Twitter,
'Pinterest': Pinterest,
'LinkedIn': LinkedIn
}
return React.createElement(Components[item], item);
}
render() {
const Element = this.renderElement(this.props.id)
return
(
<div>
{Element}
</div>
)
}
}
export default SocialMediaBox;
Thanks for the question and answers; alongside the answers given in Dynamic tag name in jsx and React they helped me to find a solution in my context (making a functional component in Gatsby with gatsby-plugin-react-svg installed):
import React from "react"
import FirstIcon from "../svgs/first-icon.inline.svg"
import SecondIcon from "../svgs/second-icon.inline.svg"
import ThirdIcon from "../svgs/third-icon.inline.svg"
const MyComponent = () => {
const sections = [
{ heading: "First Section", icon: () => <FirstIcon /> },
{ heading: "Second Section", icon: () => <SecondIcon /> },
{ heading: "Third Section", icon: () => <ThirdIcon /> },
]
return (
<>
{sections.map((item, index) => {
const Icon = item.icon
return (
<section key={index}>
<Icon />
<h2>{item.heading}</h2>
</section>
)
})}
</>
)
}
export default MyComponent
As mine is a Gatsby project I used the above mentioned plugin, but it itself process svgs with svg-react-loader so the basic principle should work in any React project using this package.