REACT - Image gallery - html

I'm retrieving images from the database in REACT and have created a holder for an image with thumbnails at the bottom.
I would like to know how I can make the interface behave like eCom sites, whereupon clicking the thumbnail, its respective image is loaded in the bigger area.
Below is the REACT code.
import React from "react";
import { Link } from "react-router-dom";
import ImageList from "../ImageList";
const ProductDetails = props => {
const images = require.context(
"../../../strapui/app/public/uploads",
true,
/\.jpg$/
);
const keys = images.keys();
const svgsArray = keys.map(key => images(key));
return(
<div className="desContainer ">
<div className="desimgContainer ">
<ImageList
styles="heroImage"
imagePath={props.selectedItem[0].image[0]}
svgsArray={svgsArray}
/>
</div>
<div className="thumbs">
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[0]}
svgsArray={svgsArray}
/>
</div>
<div className="thumbs">
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[1]}
svgsArray={svgsArray}
/>
</div>
<div className="thumbs">
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[2]}
svgsArray={svgsArray}
/>
</div>
</div>
);
};
export default ProductDetails;
The images are pulled from the database using the following code
import React from "react";
const ImageList = props => {
if (
props.imagePath === undefined ||
props.imagePath === null ||
props.imagePath.length === 0
)
return null;
const path = props.svgsArray.find(
str => str.indexOf(props.imagePath.hash) > 1
);
return <img src={path} alt={props.imagePath.hash} className={props.styles} />;
};
export default ImageList;
I was wondering if I could use a switch case to show the image when a thumbnail is clicked?
will it work? if it will, can you pls direct me how?

Use onClick event and attach it with some function which should do some code magic.
for e.g:
largeSizeImage () {
/* some code logic */
}
return (
<div className="thumbs" onClick={largeSizeImage()}>
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[1]}
svgsArray={svgsArray}
/>
</div>
)

Related

How can I use the useState function as a toggle method?

import React, { useEffect, useState } from "react";
import './Menu.css'
export default function Menu() {
const [classes, setClasses] = useState('container')
return (
<div>
<p>Click on the Menu Icon to transform it to "X":</p>
<div className={classes} onClick={() => setClasses("container change")}>
<div className="bar1"></div>
<div className="bar2"></div>
<div className="bar3"></div>
</div>
</div>
)
}
It works now because of one of the given solutions, but when I click on the icon again, it doesn't go back to the original state. How can I fix that?
I got this menu example from here: https://www.w3schools.com/howto/howto_css_menu_icon.asp
You might want to use useState hook to dynamically change your element's class name.
import React from 'react'
import './Menu.css'
export default function Menu() {
const [classes, setClasses] = useState('container')
return (
<div>
<p>Click on the Menu Icon to transform it to "X":</p>
<div className={classes} onClick={() => setClasses("container change")}>
<div className="bar1"></div>
<div className="bar2"></div>
<div className="bar3"></div>
</div>
</div>
)
}
Something like this maybe. But I'm sure there are other ways too.
if you want to toggle back and forth;
setClasses(prevClasses => {
if (prevClasses = "container") return "container change"
else { return "container" }
})
YOu can use state to check what is the current state (user clicked or not), and then can plan behavior accordingly:
export default function Menu() {
const [toggle, setToggle] = useState(false);
return (
<div>
<p>Click on the Menu Icon to transform it to "X":</p>
<div
// If toggle is true, set className to 'change' else 'contianer'
className={toggle ? 'change' : 'container'}
// toggle state (toggle) on click
onClick={() => setToggle(prevToggle => !prevToggle)}
>
<div className='bar1'></div>
<div className='bar2'></div>
<div className='bar3'></div>
</div>
</div>
);
}

React mapping fetched data

I'm trying to fetch some data from my database with some simple to-dos. However I cant seem to map them out into a list on my site.
I keep getting errors like: todoFromServer.map is not a function or that todoFromServer is not an array etc.
My current code looks like this:
import apiFacade from "../api/apiFacade";
import React, { useState, useEffect } from "react";
import {Form, FormGroup, Label, Input, Button} from "reactstrap"
export default function SecurePage() {
const [todoFromServer, setTodoFromServer] = useState("Waiting...");
useEffect(() => {
apiFacade.getTodo().then((data) => setTodoFromServer(data));
}, []);
return (
<div className="container-fluid padding">
<div className="row">
<div className="col-3"></div>
<div className="col-6 text-center">
<Form>
<FormGroup>
<h3 className="mt-5">Todos</h3>
<Input type="text" placeholder="Enter Todo"></Input>
</FormGroup>
<Button type="submit">Add</Button>
</Form>
<div>
{todoFromServer.map(() => (
<div>{todoFromServer.todoText}</div>
))}
</div>
</div>
</div>
</div>
);
}
The data I trying to fetch should come out as json looking like this:
I'm kind of lost.. Hope someone can help me out
to be clear - I want the data mapped out on a list with a delete button next to it...
const [todoFromServer, setTodoFromServer] = useState([]); // <=== initialize this as an empty array.
useEffect(() => {
apiFacade.getTodo().then((data) => setTodoFromServer(data)); // Make sure data returned from Promise resolve is indeed an array
}, []);
You want to read todoText of each todo's inside your array item so you would do something like this.
{todoFromServer.length ? todoFromServer.map((todo) => (
<div>{todo.todoText}</div>
)) : "Waiting..."}
For additional reference, take a look at Array.map usage here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

React JSON - Display data in a Card Slider

I have a local JSON file which I've converted to Javascript.
I am able to fetch the data by importing the JS file into my App.js.
This is my App.js file:
import React, { Component } from "react";
import CardData from "./data/db";
import "./App.css";
class App extends Component {
constructor() {
super();
this.state = {
CardData
};
}
render() {
return (
<div>
{this.state.CardData.map(cards => (
<div className="card">
<span>{cards.title}</span>
<br />
<span>{cards.subtitle}</span>
<br />
</div>
))}
</div>
);
}
}
export default App;
I want to be able to show 3 Cards, and then have the option to slide across to the remaining cards.
Something like this
However I am only able to show it in one div, is there a way to do it in the way I've called the JSON or is there a way to separate the JSON data by their ID?
Since you are looking for a simpler way to achieve the same result I would suggest switching your App to a stateless component, as it is never updating/using any state value :
import React from "react";
import CardData from "./data/db";
import "./App.css";
const App = props => (
<React.Fragment> //A fragment will not appear in your DOM
{CardData.map(({ title, subtitle }, index) => ( //Deconstructs each cards
<div className="card" key={index}>
<span>{title}</span>
<br />
<span>{subtitle}</span>
<br />
</div>
))}
</React.Fragment>
)
export default App;
But this component will never be able to render anything else than this specific JSON file, if you want it to be more generic, you should send your data via the component's props :
import React from "react";
import "./App.css";
const App = ({ cards }) => (
<React.Fragment>
{cards.map(({ title, subtitle }, index) => (
<div className="card" key={index}>
<span>{title}</span>
<br />
<span>{subtitle}</span>
<br />
</div>
))}
</React.Fragment>
)
export default App;
And in your parent component :
import CardData from "./data/db";
const Parent = props => <App cards={CardData}/>
You should also not forget about keys when mapping elements, as every mapped component should have a unique and persistent key.

Using a image path in a JSON object to dynamically render image in component

I am running into an issue that seems to be "similar" to other issues dealing with a JSON object in React, though I can not seem how to translate these answers. I am creating a small game inside of React (not React Native). I am able to call my JSON object and pull text, though, when it comes to pulling an image path, it will not render. To get a basic idea of this game (that was in HTML/ JS) take a look at this link for the overall functionality.
Here is the Issue, I have a dynamically rendering set of objects based on state in the parent Component (GameLogic.js). I am then passing state down to the great-great-grandchild elements where it will render a two photos. These photo paths are stored in a local JSON file (I can read strings from the characters.json file in a console.log at this level). Though it can read the path (via console.log), it is not rendering these images. I am how ever able to render these images as long as I am not stringing together a long dynamic path.
Here is the file structure:
-components folder
|-GameLogic.js (parent element that handles the render)
|-Bandersnatch.js (child element)
|-NewComponents folder
|-ImageContainer.js (grandChild element)
|-ImageSquare.js (great grandChild element)
-images folder
|-snatch_images folder (yes... I know how bad this sounds...)
|-escape_snatch.png
|-The rest of the images (there are about 20)
-characters.json
-App.js
JSON example: I need the file path at Array[0].scene[0].choiceOneImg
[
{
"name": "Giraffe",
"alive": true,
"active": true,
"staticImg": "images/characters/static/static_giraffe.png",
"animatedImg": "images/characters/animated/animated_giraffe.png",
"cagedImg": "images/characters/caged/caged_giraffe.png",
"scene": [
{
"used": false,
"backgroundImg": "images/BG_images/zooBG.png",
"question": "........." ,
"answerTrue": ".......",
"answerFalse": ".......",
"choiceOne": "RUN FOR IT!",
"choiceTwo": "Stay loyal",
"choiceOneImg": "../images/snatch_images/escape_snatch.png",
"choiceTwoImg": "images/snatch_images/stay_snatch.png",
"incorrectResult": 0,
"correctAnswer": "choiceOne",
"correct": true
},
Here is the Parent, GameLogic.js that passes the currentCharacter, sceneLocation from the state that is constantly changing:
import React, { Component } from "react";
import Snatch from "./Bandersnatch";
import characters from "../characters.json";
class GameLogic extends Component {
state ={
unlockedCharacters : 0,
currentCharacter : 0,
sceneLocation : 0,
points : 0,
showCaracterSelect: true,
showMessage: false,
showSnatch: false,
showCanvas: false,
}
componentDidMount() {
}
render() {
return (
<Snatch
sceneLocation = {this.state.sceneLocation}
currentCharacter = {this.state.currentCharacter}
choiceOneAlt = "ChoiceOne"
choiceOneImg = {characters[this.state.currentCharacter].scene[this.state.sceneLocation].choiceOneImg}
choiceTwoAlt = "ChoiceTwo"
choiceTwoImg = {characters[this.state.currentCharacter].scene[this.state.sceneLocation].choiceTwoImg}
/>
)
}
}
export default GameLogic;
Then this is passed to the child component, Bandersnatch.js:
import React, { Component } from "react";
import characters from "../characters.json";
import { HeaderH2, ImageContainer, ImageSquare, ImageText, ProgressBar, Timer } from "./NewComponents/AllComponents";
const Snatch = (props) => {
return (
<>
<title>Decision Time</title>
<div className="w3-container">
<div className="container">
<HeaderH2 text="What Would You Like To Do?" />
<div className="row">
<ImageContainer
sceneLocation = {props.sceneLocation}
currentCharacter = {props.currentCharacter}
choiceOneAlt = {props.choiceOneAlt}
choiceOneImg = {props.choiceOneImg}
choiceTwoAlt = {props.choiceTwoAlt}
choiceTwoImg = {props.choiceTwoImg}
/>
{/* <ProgressBar /> */}
{/* <ImageText
sceneLocation = {props.sceneLocation}
currentCharacter = {props.currentCharacter}
/> */}
</div>
</div>
</div>
</>
);
}
export default Snatch;
Which is then passed to ImageContainer:
import React, { Component } from "react";
import ImageSquare from "./ImageSquare";
import characterObject from "../../characters.json";
const ImageContainer = (props) => {
return (
<>
<div className="col-md-6 optionOneclassName">
<ImageSquare
imgsrc={props.choiceOneImg}
altText={props.choiceOneAlt}
/>
</div>
<div className="col-md-6 optionTwoclassName">
<ImageSquare
imgsrc={props.choiceTwoImg}
altText={props.choiceTwoAlt}
/>
</div>
</>
)
};
export default ImageContainer;
And then finally accepted in the ImageSquare.js:
import React from "react";
const ImageSquare = (props) => { // passing in the img src
return (
<img src={props.imgsrc} alt={props.altText} height="600" width="600" />
)
};
export default ImageSquare;
Thank you so much for your help! I am not sure if it is easier, but the repo is here
Use require to load the image. Passing path to img directly won’t work. Either you need import or require to load the image
You need to add ../ before path
Change
import React from "react";
const ImageSquare = (props) => { // passing in the img src
return (
<img src={props.imgsrc} alt={props.altText} height="600" width="600" />
)
};
export default ImageSquare;
To
import React from "react";
const ImageSquare = (props) => { // passing in the img src
const path = "../"+props.imgsrc;
return (
<img src={require(path)} alt={props.altText} height="600" width="600" />
)
};
export default ImageSquare;

React: Create a new html element on click

I've used React for a couple of weeks now but I have this simple problem that I can't seem to wrap my head around. It's about creating new html elements.
I would just like to know in general if the way that I went about it, is the "right way" or is there another preferred way to create new html element with a click function.
For some reason this problem took awhile for me to figure out and it still feels a bit strange, that's why I'm asking.
Thanks in advance!
import React, { Component } from 'react';
import './Overview.css';
import Project from './Project';
class Overview extends Component {
constructor() {
super()
this.state = {
itemArray: []
}
}
createProject() {
const item = this.state.itemArray;
item.push(
<div>
<h2>Title</h2>
<p>text</p>
</div>
)
this.setState({itemArray: item})
//console.log(this.state)
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return <div className="box" key={index}>{item}</div>
})}
</div>
</div>
);
}
}
export default Overview;
No, this is not a correct approach. You shouldn't be generating HTML elements like that, nor keep them in state - it is against React to manipulate DOM like that. You won't be able to utilize Virtual DOM is the first thing that I can think of.
What you should do instead is keep all data that is needed for rendering in state and then generate the HTML element from there, for instance
createProject() {
const item = this.state.itemArray;
const title = '';
const text = '';
item.push({ title, text })
this.setState({itemArray: item})
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return (
<div className="box" key={index}>
<div>
<h2>{item.title}</h2>
<p>{item.text}</p>
</div>
</div>
)
})}
</div>
</div>
);
}