Image onError Handling in React - javascript

I am trying to grab the maxresdefault of multiple YouTube thumbnails. Some YouTube videos simply don't have a high res thumbnail photo so I want to catch that with an onError prop on an img element. For some reason my function is not triggering when I get the 404 img error. Any ideas? Thanks in advance.
class FeaturedVideo extends Component<Props> {
addDefaultSrc = (e) => {
e.target.src = this.props.background.replace("maxresdefault", "hqdefault")
}
renderVideo = (props) => (
<div
style={{
width: "100%",
height: "100%",
backgroundSize: "contain",
}}
className="featured-community-video"
>
<img onError={this.addDefaultSrc} src={props.background} alt="" />
<div className="featured-community-video-title">
<h2 style={{ fontSize: "0.8em" }}>WATCH</h2>
<h1 style={{ fontSize: props.titleFontSize }}>{props.title}</h1>
</div>
</div>
)
render() {
return (
<div
key={this.props.postId}
style={{
width: this.props.width,
height: "50%",
}}
className="featured-community-video-container"
>
<Link to={routeCodes.POST_DETAILS(this.props.postId)}>
{this.renderVideo(this.props)}
</Link>
</div>
)
}
}

To achieve this, I would suggest rendering the <img /> based on the state of your <FeatureVideo/> component.
You could for instance, create an Image object, attempt to load the background image and by this, reliably determine if the image fails to load. On the images success or failure to load, you would then setState() on your <FeaturedVideo/> component with the appropriate background value which would instead be used to rendering you actual <img/> element:
class FeaturedVideo extends Component<Props> {
componentDidMount() {
if(this.props.background) {
// When component mounts, create an image object and attempt to load background
fetch(this.props.background).then(response => {
if(response.ok) {
// On success, set the background state from background
// prop
this.setState({ background : this.props.background })
} else {
// On failure to load, set the "default" background state
this.setState({ background : this.props.background.replace("maxresdefault", "hqdefault") })
}
});
}
}
// Update the function signature to be class method to make state access eaiser
renderVideo(props) {
return <div
style={{
width: "100%",
height: "100%",
backgroundSize: "contain",
}}
className="featured-community-video">
{/* Update image src to come from state */}
<img src={this.state.background} alt="" />
<div className="featured-community-video-title">
<h2 style={{ fontSize: "0.8em" }}>WATCH</h2>
<h1 style={{ fontSize: props.titleFontSize }}>{props.title}</h1>
</div>
</div>
}
render() {
return (
<div
key={this.props.postId}
style={{
width: this.props.width,
height: "50%",
}}
className="featured-community-video-container"
>
<Link to={routeCodes.POST_DETAILS(this.props.postId)}>
{this.renderVideo(this.props)}
</Link>
</div>
)
}
}
Hope that helps!

Related

React-slick - how to change custom arrow colour based on the page number

There are 2 custom arrows which are previous-arrow and next-arrow
There are also total 3 pages with 3 slides per page
What I want to do is whenever the page is the first page/last page, the previous arrow/next arrow will apply filter in svg, which means the previous arrow/next arrow will become black color
<img
…
filter:
"invert(3%) sepia(7%) saturate(7029%) hue-rotate(94deg) brightness(86%) contrast(93%)"
}}
/>
For example, if it’s the first page, the previous arrow will be filtered while next arrow will NOT be filtered.
Is it possible to do it?
App.js
import "./styles.css";
import React from "react";
import Slider from "react-slick";
import ArrowPrevious from "./arrow-previous.svg";
import ArrowNext from "./arrow-next.svg";
const ArrowButton = ({ imgSrc, imgAlt, onClick }) => {
return (
<button
onClick={onClick}
style={{ backgroundColor: "transparent", border: "none" }}
>
<img
src={imgSrc}
alt={imgAlt}
style={{
width: "50px",
height: "50px",
filter:
"invert(3%) sepia(7%) saturate(7029%) hue-rotate(94deg) brightness(86%) contrast(93%)"
}}
/>
</button>
);
};
export default function App() {
const settings = {
dots: true,
infinite: false,
speed: 500,
slidesToShow: 3,
slidesToScroll: 3,
prevArrow: <ArrowButton imgSrc={ArrowPrevious} imgAlt="previous-button" />,
nextArrow: <ArrowButton imgSrc={ArrowNext} imgAlt="next-button" />,
beforeChange: (current, next) => {
console.log(next);
}
};
return (
<div>
<Slider {...settings}>
<div>
<h3>1</h3>
</div>
<div>
<h3>2</h3>
</div>
<div>
<h3>3</h3>
</div>
<div>
<h3>4</h3>
</div>
<div>
<h3>5</h3>
</div>
<div>
<h3>6</h3>
</div>
<div>
<h3>7</h3>
</div>
</Slider>
</div>
);
}
Codesandbox
https://codesandbox.io/s/focused-dubinsky-9onwe?file=/src/App.js
See codesandbox
https://codesandbox.io/s/inspiring-austin-k9i86?file=/src/App.js:510-527
You just have to look for the onClick !== null
I broke your arrows into 2 separate components though i.e. ArrowButtonNext and ArrowButtonPrevious
const ArrowButtonPrevious = ({ imgSrc, imgAlt, onClick }) => {
return (
<button
onClick={onClick}
style={{ backgroundColor: "transparent", border: "none" }}
>
<img
src={imgSrc}
alt={imgAlt}
style={{
width: "50px",
height: "50px",
filter:
onClick === null
? "invert(93%) sepia(7%) saturate(7029%) hue-rotate(94deg) brightness(86%) contrast(93%)"
: "none"
}}
/>
</button>
);
};
const ArrowButtonNext = ({ imgSrc, imgAlt, onClick }) => {
return (
<button
onClick={onClick}
style={{ backgroundColor: "transparent", border: "none" }}
>
<img
src={imgSrc}
alt={imgAlt}
style={{
width: "50px",
height: "50px",
filter:
onClick === null
? "invert(93%) sepia(7%) saturate(7029%) hue-rotate(94deg) brightness(86%) contrast(93%)"
: "none"
}}
/>
</button>
);
};
I have Checked on Code sandbox and inspect the arrow ,you can change only background Color or else you need to import arrow icon from Material UI or Bootstrap

React Lightbox Component doesn't open

I admit I am very new to React and still getting my head around things, but I am using the react-lightbox-component to display images in a grid and that works just fine but the onClick event doesn't do anything then it calls the toggleLightBox function.
I know the onClick is working as I tested it against a simple console.log, what am I missing to be able to display the image in a lightbox when clicked?
const App = ({ compile }) => (
<div style={{margin: 20}}>
<header style={{textAlign: "center"}}>
<h1><Words animate>Art</Words></h1>
</header>
<body>
<h3>Android</h3>
</body>
<Lightbox
images={androidimages}
renderImageFunc={(idx, image, toggleLightbox, width, height) => {
return (
<img
key={idx}
src={image.src}
style={{width: '150px', height: '200px', margin: '20px'}}
onClick={
toggleLightbox.bind(null, idx)
}
/>
)
}
}
/>
</div>
);

How to map over array of ImageURLs and display each?

I am implementing a multiple image upload. The user should have the possibility to select the images and see a small preview of the selected images. I store the image URL in an array and iterate the url to display the images. The problem is that the image is not updated in the preview. I.e. the first, second, etc. Image is the same (see attached picture). When I look at the image URL as text, it becomes clear that the links are different and lead to different images (see picture). So I don't understand why the same image is displayed overall iterations although the url is different.
export class ImageUploadView extends Component {
constructor(props) {
super(props);
this.onChangeImage = this.onChangeImage.bind(this);
this.fileObj = [];
this.fileArray = [];
}
onChangeImage (e) {
this.fileObj.push(e.target.files)
for (let i = 0; i < this.fileObj[0].length; i++) {
this.fileArray.push(URL.createObjectURL(this.fileObj[0][i]))
}
}
render() {
return (
<React.Fragment>
<div className="card card-body mt-4 mb-4">
<h1>Add Image</h1>
<div>
<input type="file" onChange={this.onChangeImage} multiple ref={this.inputRef} />
{(this.fileArray || []).map((url) => (
<MDBRow key={url} style={{ margin: '10px', display: 'inline-block' }}>
<MDBCol>
<MDBCard style={{ width: '5rem' }}>
<MDBCardImage className="img-fluid" src={url} waves />
<MDBCardBody>
<p> {url} </p>
</MDBCardBody>
</MDBCard>
</MDBCol>
</MDBRow>
))}
</div>
</div>
</React.Fragment>
);
}
}

React Carousel with text

I am using https://www.npmjs.com/package/react-multi-carousel in one of my react project and I have implemented it and it works fine.
I am now in the need to implement the text (legend) over the carousel exactly like https://codesandbox.io/s/lp602ljjj7 which uses another package but I need that scenario and not that package because my need is different (Using nextjs so multi-carousel supports ssr and hence using it).
My only need is need to know how to implement the legend text over the carousel image using react-multi-carousel.
My code example: https://codesandbox.io/s/react-multi-carousel-playground-bt3v7
Change the structure of the return statement to the following structure.
Then you can store the value of the legend in the image array and pass the value to the p tag
const Simple = ({ deviceType }) => {
return (
<Carousel
ssr
partialVisbile
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
>
{images.slice(0, 5).map((image, index) => {
return (
<div key={index} style={{ position: "relative" }}>
<img
draggable={false}
alt="text"
style={{ width: "100%", height: "100%" }}
src={image}
/>
<p
style={{
position: "absolute",
left: "50%",
bottom: 0,
color: "white",
transform: " translateX(-50%)"
}}
>
Legend:{index}.
</p>
</div>
);
})}
</Carousel>
);
};
export default Simple;
Live demo
You should change array structure like below.
const images = [
{
image: "https://images.unsplash.com/photo-1549989476-69a92fa57c36?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60",
text: "First image",
}
];
Then inside loop just add text and style it our way!
const Simple = ({ deviceType }) => {
return (
<Carousel
ssr
partialVisbile
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
>
{images.map(image => {
return (
<div>
<img
draggable={false}
alt="text"
style={{ width: "100%", height: "100%" }}
src={image.image}
/>
{/* Have to style so this should see over the image */}
<span>{image.text}</span>
</div>
);
})}
</Carousel>
);
};

How can I add Vanilla JS to my react app to create Image Modal pop-ups?

I am creating an image grid for a website Im making using React and I wanted to add a modal functionality to view each image as a modal pop-up. For this I need to use some Vanilla JS in combination with CSS and HTML. This is the js I want to use:
// Select the modal
var modal = document.getElementById('myModal');
// Get the image inside the modal
var img = document.getElementsByClassName('image');
var modalImg = document.getElementById("img01");
var captionText = document.getElementById("caption");
img.onclick = function(){
modal.style.display = "block";
modalImg.src = this.src;
captionText.innerHTML = this.alt;
}
// Select the close button
var span = document.getElementsByClassName("close");
// Close Button
span.onclick = function() {
modal.style.display = "none";
}
How can I integrate it with the rest of the app?
EDIT:
This is the component I am working on:
import React from "react";
import "../stylesheets/Photos.scss";
const Photos = () => {
return (
<div className="photos_container">
<h1 className="photos_title">PHOTO GALLERY</h1>
<div className="row">
<div className="column">
<img className='image'src="../assets/photos_page/photo2.jpg"/>
<img className='image' src="../assets/photos_page/photo3.jpg"/>
</div>
<div className="column">
<img className='image' src="../assets/photos_page/photo4.jpg"/>
<img className='image' src="../assets/photos_page/photo5.jpg"/>
<img className='image' src="../assets/photos_page/photo6.jpg"/>
</div>
<div className="column">
<img className='image' src="../assets/photos_page/photo7.jpg"/>
<img className='image' src="../assets/photos_page/photo8.png"/>
<img className='image' src="../assets/photos_page/photo9.jpg"/>
</div>
<div className="column">
<img className='image' src="../assets/photos_page/photo10.png"/>
<img className='image' src="../assets/photos_page/photo11.jpg"/>
</div>
</div>
{/* modal */}
<div id='mymodal' className='modal'>
<span className='close'>×</span>
<img className='modal-content' id="img01"/>
<div id='caption'></div>
</div>
</div>
);
};
export default Photos;
Someone beat me to it, but I will still leave my version of the answer here in case you find it helpful. I would not select document elements in the mentioned way, because there is simpler and more declarative way. What you want to do is change this component into a stateful one, which contains a state property like "showmodal". When it is true, the modal should be shown. Then you can toggle the state and set the image which was clicked on by using this.setState({ showmodal: true, slectedImage: yourImage }) in your onClick function. A simple absolutely positioned container should do the trick for the modal container.
Here is an example of what such a component would look like.
const images = [
{
uri:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTm3CuNzOrmWhos3MXI0v_KkyO_xrZ5Y8hFxSYGj_6OtUygZUUX"
},
{
uri:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQR_Fg_lMrPjMa1duHXnvFnZOtsn883LGLD7c2-CwKdfFXAoveQnQ"
}
];
class App extends React.Component {
state = {
showmodal: false,
selectedImage: undefined
};
render() {
//
return (
<div style={{ height: "100vh"}}>
{images.map(i => (
<img
key={i.uri}
src={i.uri}
style={{ margin: 5 }}
onClick={() => this.setState({ showmodal: true, selectedImage: i })}
/>
))}
{this.state.showmodal &&
<div
onClick={() => this.setState({ showmodal: false })}
style={{
display: 'flex',
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: 'center', alignItems: 'center'
}}
>
<div style={{ height: 300, width: 500, backgroundColor: '#fff' }}>
<img src={this.state.selectedImage.uri} style={{ flex: 1}} />
<h3>Place any content here</h3>
</div>
</div>}
</div>
);
}
}
React.render(<App />, document.getElementById("app"));
I did not refactor out the styles so you can see what each div is doing as a super basic example which you can check out in codepen. Also, I agree with viz above in trying to refactor you columns so that you can render them the exact same way, and reduce duplication.
Hope it helps!
Everything in react is javascript, so
<div style={{display: this.state.showModal ? 'block' : 'none' }}>
<span onClick={() => {this.state.showModal = false;}}>×</span>
<img src={this.state.currentImage}/>
<div>{this.state.currentCaption}</div>
</div>
and then set respective currentImage and currentCaption with setState upon the image click.
You should also consider programmatically composing the column & images' jsx so that it can be concise.

Categories