How to load a specific photo with dynamic URL with react.js - reactjs

I have a component where a list of pictures is rendered and it works perfectly fine :
import { Component} from 'react'
import Header from '../Home/Header'
import Footer from '../Home/Footer'
import PhotoItems from './objet'
class Photos1930 extends Component {
render() {
return (
<div>
<Header />
<h2 className='titre bloc'>Photos 1930</h2>
<div className='bloc bloc__photo'>
{PhotoItems.map((val, key) => {
let id = val.id
let url = val.url
let lienImage = "/galerie/:" + (val.id)
return <div key={id}>
<a href={lienImage}>
<img className='photo' alt='Photo Charles-Quint' src={url}></img>
</a>
</div>
})}
</div>
<Footer />
</div>
)
}
}
export default Photos1930
I want to create an other component where i can load a specific picture when user click on a picture from the precedent list. I use the same logic but for some reason the picture doesn't load. I don't have any errors in my console but on my page i just have the standard icon for image with my alt.
All the pictures are on public folder.
I just don't understand why is it working on one component but not on the other one.
import { Component } from 'react'
import Header from '../Home/Header'
import Footer from '../Home/Footer'
import PhotoItems from './objet'
const url = window.location.pathname
const justId = parseInt((url.split(':')[1]))
function specificId(photo) {
return photo.id === (justId)
}
let justUrl = (PhotoItems.find(specificId).url)
console.log(justUrl)
class PickPhoto extends Component {
render() {
return (
<div>
<Header />
<div>
<h1>{justId}</h1>
<img className="bigPhoto" alt="Charles-Quint" src={justUrl}></img>
</div>
<Footer />
</div>
)
}
}
export default PickPhoto
EDIT1 : Here's my github repo : https://github.com/FranMori/CharlesQuint
and here's my netlify link : https://stoic-bohr-810e13.netlify.app/
You can click on "Galerie Photos" and then click on any picture to see the problem.

in your repo, this.justUrl is undefined. You need to add justUrl in the component's state and update it dynamically inside componentDidMount like below. I also added a / in src={/${this.state.justUrl}}
import { Component } from 'react'
import Header from '../Home/Header'
import Footer from '../Home/Footer'
import PhotoItems from './objet'
class PickPhoto extends Component {
constructor() {
super()
this.state = { justUrl: "" };
}
componentDidMount() {
const url = window.location.pathname
const justId = parseInt((url.split(':')[1]))
function specificId(photo) {
return photo.id === justId
}
let justUrl = (PhotoItems.find(specificId).url)
console.log(justUrl)
this.setState({justUrl})
}
render() {
return (
<div>
<Header />
<div>
<h1>{this.justId}</h1>
<img className="bigPhoto" alt="Charles-Quint" src={`/${this.state.justUrl}`}></img>
</div>
<Footer />
</div>
)
}
}
export default PickPhoto

Related

Extract Data from API and show in another page

This question may sound silly to some people, but I am really confused on how to do it
I have 3 file: App.js, HomePage.js and Profile.js
App.js :
import React from "react"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/HomePage";
import Profile from "./components/Profile"
function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/profile/:profileId" component= {Profile} />
</Switch>
</Router>
);
}
export default App;
From here, the default page it will go to is HomePage.js
HomePage.js:
import React, { Component } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
class HomePage extends Component {
constructor() {
super();
this.state = {
userData: [],
}
}
componentDidMount() {
axios.get("XXXXXXXX").then((response) => {
const userDataList = response.data.users;
this.setState({
userData: userDataList
})
})
}
render() {
const userGrid = this.state.userData.map((user, index) => {
return (
<div key={index}>
<Link to={`/profile/${user.id}`}>
<img src={user.profilepicture} />
<p>{user.name}</p>
</Link>
</div>
)
})
return (
<div className="App">
<div className="card">
<div className="card__top">
<span className="card__title">
<p>Select An Account</p>
</span>
</div>
<div className="card__bottom">
<div className="card__table">
{userGrid}
</div>
</div>
</div>
</div>
)
}
}
export default HomePage;
In HomePage.js, I am able to show the profile picture and name of the user from API.
In the next page which is Profile.js , I am able to print the ID of the user.
Profile.js:
import React, { Component } from "react";
class Profile extends Component{
componentDidMount(){
const uid = this.props.match.params.profileId;
}
render() {
console.log(this.props.match);
return(
<h1>{this.props.match.params.profileId}</h1>
)
}
}
export default Profile;
As you can see I am printing the ID of user.
Here I also want to show the Profile Picture of the user which I selected in HomePage.js
This I am not able to do it.
JSON file:
{ - users: [-{id:1, name:"abc", profilepicture: "xxxxx.jpeg"}, ]}
You need to store a global state in your applicattion, which you can access from every connected component. This is a more complex topic. redux is a good framework to handle your global state changes.
Here is a tutorial: https://appdividend.com/2018/06/14/how-to-connect-react-and-redux-with-example/
I found it pretty hard to learn redux, but in the end it takes away a lot of pain. Because this is a problem you gonna have in every app you build with react.
You need use Context API o redux
Example context API: https://ibaslogic.com/react-context-api/
Context's well to little projects, but Redux performs better.
App.js
import React from "react"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/HomePage";
import Profile from "./components/Profile"
import { UsersProvider } from "./UsersProvider.js";
function App() {
return (
<Router>
<UsersProvider>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/profile/:profileId" component= {Profile} />
</Switch>
</UsersProvider>
</Router>
);
}
export default App;
UsersContext.js
import React, { Component } from "react"
const UsersContext = React.createContext();
const UsersProvider = UsersContext.Provider;
const UsersConsumer = TodosContext.Consumer;
class MyContext extends Component {
state = {
value: null,
};
setValue = (value) => {
this.setState({ value });
};
render() {
return (
<UsersProvider value={{ setValue, value }}>{this.props.children}
</UsersProvider>
)
}
}
export { UsersContext, UsersProvider, UsersConsumer }
HomePage.js
import React, { Component } from "react";
import axios from 'axios';
class HomePage extends Component {
componentDidMount() {
axios.get("XXXXXXXX").then((response) => {
const userDataList = response.data.users;
// updating your context
this.props.context.setValue(userDataList);
})
}
render() {
const userGrid = this.props.context.value.map((user, index) => {
return (
<div key={index}>
<Link to={`/profile/${user.id}`}>
<img src={user.profilepicture} />
<p>{user.name}</p>
</Link>
</div>
)
})
return (
<div className="App">
<div className="card">
<div className="card__top">
<span className="card__title">
<p>Select An Account</p>
</span>
</div>
<div className="card__bottom">
<div className="card__table">
{userGrid}
</div>
</div>
</div>
</div>
)
}
}
export default HomePage;
Profile.js
import React, { Component } from "react";
import { UsersConsumer } from "./UsersContext.js";
class Profile extends Component{
render() {
return(
<UsersConsumer>
{users => (
<h1>{users.value.find(user => user.id === this.props.match.params.profileId)}</h1>
)}
</UsersConsumer>
)
}
}
export default Profile;

Lazy loading ReactJS component

I'm building SPA (single page app) using React and React-Router.
"Employees" is one of the navigation menu items in the header.
According to the docs - Route-based code splitting, I'm trying to make components lazy loading like this:
import React, { Component, lazy, Suspense } from 'react';
import './App.css';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
...
// import { Employees } from './components/Employees/Employees';
const Employees = lazy(() => import('./components/Employees/Employees'));
export default class App extends Component {
state = {
...
employeesData: [
...objects with data...
]
}
render() {
return (
<BrowserRouter>
<div className="App">
...
<Suspense fallback={<div>Loading...</div>}>
<Switch>
{/* other routes here */}
<Route path="/employees/" component={
() =>
<Employees
data={this.state.employeesData}
/>
} />
</Switch>
</Suspense>
...
</div>
</BrowserRouter>
);
}
}
Employees component looks like:
import React from 'react';
import './css/Employees.css';
export const Employees = (props) => {
const { data } = { ...props };
// sort elements by name value
data.sort(
(a,b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0)
);
let items = [];
for (let i = 0; i < data.length; i++) {
items.push(
<div className="container">
<div className="imgbox">
<img className="image"
src={ data[i].image }
alt={ data[i].name }
/>
</div>
<div className="textbox">
<h4 className="name">
{ data[i].name }
</h4>
<p className="text">
{ data[i].title }
<br/>
{ data[i].text }
<br/>
{ data[i].workplace }
</p>
</div>
</div>
);
}
return (
<div className="Employees">
...
<div className="wrapper">
...
{ items }
</div>
</div>
)
};
The thing is - when clicking on the "Employees" nav item, web-page becomes blank. What am I doing wrong?
Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]
Your code should look like:
const MyComponent = lazy(() => import('./MyComponent'))
It's saying it expects the Employees component to be the default export of the file. You'll have to change it to be the default export: https://reactjs.org/docs/code-splitting.html#named-exports
Two things.
Use a render prop instead of component in this situation (Router).
Wrap in Suspense just your Employees instead of the whole Switch (inside your render prop).

Not getting props in react component

I'm not getting props in my Nav component. Odd thing is, 'this.props.history.push' is working in my other components.
The same function is working in my other components, but when I try to call the push function, I'm getting 'err in logout TypeError: Cannot read property 'push' of undefined'. The 'this.props' object is logging as '{}'.
Any help is appreciated, thank you.
import React from 'react'
import logo from 'logo.png'
import css from './Nav.module.scss'
import { Link } from 'react-router-dom'
import Cookies from 'js-cookie'
import axios from 'axios'
class Nav extends React.Component {
constructor(props) {
super(props)
this.state = {
loggedIn: false
}
console.log(this.props)
}
_handleLogout = () => {
// const self = this
console.log(this.props)
axios.get('http://localhost:8080/logout', {
withCredentials: true
})
.then(res => {
console.log(res)
console.log('logout')
if (Cookies.get('sid') === undefined) {
this.props.history.push('/')
}
console.log(this.props)
})
.catch(err => {
console.log('err in logout', err)
})
}
render() {
return (
<div className={css.nav}>
<div className={css.leftPart}>
<Link to="/">
<div className={css.brandicon}>
<img src={logo} alt="Logo" />
</div>
<div className={css.brandname}>
somebrand
</div>
</Link>
</div>
<div className={css.rightPart}>
{
Cookies.get('sid') === undefined ?
<Link to="/login">
<div className={css.loginButton}>
Login
</div>
</Link>
:
<div className={css.logoutButton} onClick={this._handleLogout}>
Logout
</div>
}
</div>
</div>
)
}
}
export default Nav
My Nav component is only referenced once in my Layout component:
import React from 'react'
import Nav from 'components/Nav/Nav'
import css from './BasicLayout.module.scss'
class Basic extends React.Component {
render() {
return (
<div className={css.page}>
<Nav />
<div className={css.content}>
{this.props.children}
</div>
</div>
)
}
}
export default Basic
history and location are special props injected by React Router's HOC withRouter
import { withRouter } from 'react-router-dom'
class Nav extends React.Component{
render(){
const { history, location } = this.props
return <div>{`I'm at ${location.pathname}`}</div>
}
}
export default withRouter(Nav)
It works for functional components as well
export const Component = withRouter(({ history, location })) =>(
<div>{`I'm at ${location.pathname}`}</div>
)

How to add a skeleton load for generated items

I'm working on a slider of items similar to Netflix but with movies, using the info recovered from the tmdb API.
I would like to add a skeleton load for each dynamically generated item using map to improve the user experience.
First of all for the skeleton loading I got a lot of inspiration from this code available on pen code that I adapted afterwards.
https://codepen.io/mxbck/pen/EvmLVp
I try a solution first here but it does not work, I voluntarily remove code that did not relate to my problem for clarity:
Slider component
import React, {PureComponent , Component } from "react";
import style from './Caroussel.css';
import MovieItem from "../components/MovieItem";
import MovieItemContainer from '../components/MovieItem/MovieItemContainer';
class Slider extends Component {
constructor(props){
super(props);
this.handleOnLeftArrowClick = this.leftArrowClick.bind(this);
this.handleOnRightArrowClick = this.rightArrowClick.bind(this);
this.state = {
sliderItems: [],
}
}
componentDidMount() {
this.updateSliderState();
this.setState({
totalItems: this.props.movieList.length,
sliderItem: this.props.movieList
})
}
componentWillMount(){
if(typeof(window) !== 'undefined') {
$(window).on('resize', this.updateSliderState.bind(this))
}
}
render(){
const { sliderItem} = this.state;
const sliderClass = cx ({
sliderMask:true,
moving
})
return(
<div className="wrapper">
<div className={style.slider}>
<div className={sliderClass} ref="slider">
{this.state.sliderItem ?
sliderItem.map((element, index) => (
<MovieItemContainer>
<MovieItem
key={index}
title={element.title}
id={element.id}
release_date={element.release_date}
url={element.backdrop_path}
/>
</MovieItemContainer>
))
:
sliderItem.map((element, index) => {
null
})
}
</div>
{
click &&
<div className={style.leftArrow} ref="leftArrow">
<IosArrowBack onClick{this.handleOnLeftArrowClick} color="black" />
</div>
}
<div className={style.rightArrow} ref="rightArrow">
<IosArrowForward onClick={this.handleOnRightArrowClick} color="black" />
</div>
</div>
</div>
);
}
}
export default Slider;
MovieItem Container component
import React, {Component} from "react";
import '../../../style/card.scss';
class MovieItemContainer extends React.Component {
render() {
return (
<div className="card">
{this.props.children}
</div>
);
}
}
export default MovieItemContainer;
MovieItem component
import React from 'react';
import Moment from 'react-moment';
import style from './MovieItem.css';
import {Link} from 'react-router';
const MovieItem = ({ url, title, release_date, id }) => {
let link ='https://image.tmdb.org/t/p/w300/'+url;
const text_truncate = (str, length, ending) => {
if (length == null) {
length = 100;
}
if (ending == null) {
ending = '...';
}
if (str.length > length) {
return str.substring(0, length - ending.length) + ending;
} else {
return str;
}
};
return (
<div className={style.sliderItem}>
<div style={{ borderBottomLeftRadius: 8, borderBottomRightRadius:8 }} className={style.sliderItemInner}>
<img style={{ borderRadius: 8 }} className={style.cover} src={link} />
<div className={style.shadow}></div>
<div className={style.titles}>
<span className={style.title}>
<Link className={style.title} to={`film/${id}`}>
{text_truncate(title,18)}
</Link>
</span>
<span className={style.release_date}>
<Moment format="YYYY">
{release_date}
</Moment>
</span>
</div>
</div>
</div>
)
}
export default MovieItem;
The MovieItem component only makes the item a movie with a background image, the title of the movie and the year of release.
In the example above, the loading is done for only one card, I would just adapt this code for each of the generated items (10 items).
I thank you in advance for your help and your answers.

React JS thumbnail gallery

I am building a component which is a gallery that consists of a main gallery image and a list of thumbnails under the main image. This component will be used across multiple pages so each page will have it own thumbnails/images. I have worked out how to get the correct images into the gallery depending on the page using redux and a store. However I cant figure out the functionality in getting the main image to change when the corresponding thumbnail is clicked, any ideas or suggestion how I could approach this?
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Gallery extends Component {
render() {
let img = this.props.bouq.map((el, index) => {
return(
<img src={"/images/" + el + ".jpg"} alt="." key={index}/>
);
})
return(
<section className="gallery">
<div className="mainImage">
<img src='/images/bouquets.jpg' alt="."/>
</div>
<div className="thumbnails">
{img}
</div>
</section>
);
}
}
const mapStateToProps = state => {
return {
bouq: state.bouquets
};
}
export default connect(mapStateToProps)(Gallery);
You can leverage the access to every individual element inside the map, onClick on an element, you can update you mainImage in your redux store.
You'll also need to create an action that updates the mainImage (depending on how you manage your redux actions)
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { updateMainImage } from './actions'
class Gallery extends Component {
updateMainImage = tumbnail => () => {
this.props.dispatch(updateMainImage(thumbnail))
}
render() {
const { mainImg, bouq } = this.props
return (
<section className="gallery">
<div className="mainImage">
<img src={`/images/${mainImage}.jpg`} alt={mainImage} />
</div>
<div className="thumbnails">
{bouq.map(thum => (
<img
key={thumb}
src={`/images/${thumb}.jpg`}
alt={thumb}
onClick={this.updateMainImage(thumb)}
/>
))}
</div>
</section>
)
}
}
const mapStateToProps = state => {
return {
bouq: state.bouquets,
mainImage: state.mainImage,
}
}
export default connect(mapStateToProps)(Gallery)

Resources