Add fallback src to image on React during server fail (S3) - javascript

I have a React + Rails app and the current S3 server issues made me realize that I don't have a proper fallback mechanism when data can't be pulled from s3. I'm trying to make it work with my locally stored images for the moment and I plan on adding it the same way to my other image tags.
My img is as follows:
errorLink() {
this.onError = null;
this.src = '/img/icons/static/credentials.svg';
}
<img alt="Icon for credentials and experience" src="/img/icons/static/credentials123.svg" onError={this.errorLink.bind(this)()}/>
the src inside the image pointing to credentials123.svg is a dummy and I've added it specifically to raise the error. But it's not updating the src of my image. How can I achieve this in react? I'd rather not show broken image links to my users next time during an Amazon outage

One of the many many solutions (since it is really based on your inner React project structure). You just keep your image url in a state somewhere as a default image. Once you get your proper image url from S3 then you will replace default one in state with a new one you got.
const DEFAULT_IMAGE = '/img/icons/static/default.svg';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
url: DEFAULT_IMAGE
};
}
render() {
return (
{/* ... some UI here ... */}
<img
alt="Icon for credentials and experience"
src={this.state.url}
/>
);
}
// ....
_someAsync = () => {
// some async logic here
// In here in 5 seconds state will be updated by replacing default url
// with new url image link
setTimeout(() => this.setState({
loaded: true,
url: '/img/icons/static/credentials.svg'
}), 5000);
};
}

One simple way to do it is
Make sure you put/set the value this.state = {image: 'image-to-load.jpeg'} in render.
<img src={this.state.image} ref={image => this.image = image} onError={() => this.image.src = '/alternate-failover.jpg'
It worked for me. Small, precise, and reliable code
Ref: https://stackoverflow.com/a/42848501/10231115

Related

How to preload image in a React JS component?

I'm currently rendering a child component when a signInError occurs. The signInError is stored in the parent component and if it's not null, it renders the <SignInError/> component, as per the code below:
ParentComponent.js
// Some code...
render() {
return(
<div>
<SignInForm
doSignIn={this.doSignIn}
resetSignInError={this.resetSignInError}
signInError={this.state.signInError}
/>
{this.state.signInError && <SignInError/>}
</div>
);
}
So far, so good, here's the child component SignInError.js
import React from 'react';
import RoundImage from '../../../UI/Common/RoundImage';
import Newman from '../../../../assets/newman-min.png';
class SignInError extends React.Component {
constructor(props){
super(props);
}
componentDidMount(){
const img = new Image();
img.src = Newman;
}
render(){
return(
<div>
<div>
<RoundImage src={img.src}/> // <--- img is undefined here!!!
</div>
<div>
Hello... Newman!
</div>
</div>
);
}
}
export default SignInError;
RoundImage.js
import React from 'react';
const RoundImage = (props) => {
return (
<div>
<img src={props.src}/>
</div>
);
}
export default RoundImage;
How to preload images in React.js?
This question's answer (link above) here on Stack Over flow tells me to create the img object inside the componentDidMount() method to force the browser to load it. And so I did, as you can see from the code above. But now, when I try to pass it as a prop to my grand-child component inside my render method, I can't access the img, because it was defined inside of another method.
What's the best way around this? I just need the image to be loaded and to be displayed together with the error message. Otherwise the error message will show before the image, if your browser hasn't cached it yet. Thanks for the help.
Image download happens in the browser. Rendering to the DOM also happens in the browser.
By preloading, do you mean that you want that the component renders only when the image is ready?
If so, you could do something like this:
componentDidMount() {
const img = new Image();
img.onload = () => {
// when it finishes loading, update the component state
this.setState({ imageIsReady: true });
}
img.src = Newman; // by setting an src, you trigger browser download
}
render() {
const { imageIsReady } = this.state;
if (!imageIsReady) {
return <div>Loading image...</div>; // or just return null if you want nothing to be rendered.
} else {
return <img src={Newman} /> // along with your error message here
}
}
A bit of a different approach, but if you have the image source in advance, you can add the image source to the main html file as a link ref with preload option. The browser will preload the image (with relatively low priority) and at the time your app will load the image, it should be cached in the browser memory.
<head>
..
..
<link rel="preload" href="<your_image_source_here>" as="image">
...
</head>
In this approach, we separate the preloading process from the code. It is more relevant when you have the image source in advance (and not dynamically) and when you don't need to cache a large amount of images (will be a bit messy to add a large list of links in the html head, although possible)
you can learn more about link preloading here: Preloading content with rel="preload"
In my case I start with an initial src attribute for my images and wanted to delay changing them after the image has been loaded by the browser, so I wrote the following hook in Typescript:
import { useEffect, useState } from 'react';
export const useImageLoader = (initialSrc: string, currentSrc: string) => {
const [imageSrc, _setImageSrc] = useState(initialSrc);
useEffect(() => {
const img = new Image();
img.onload = () => {
_setImageSrc(currentSrc);
};
img.src = currentSrc;
}, [currentSrc]);
return [imageSrc];
};

Image doesnt change although the src content changes

<img [(src)]="user.photo_url" alt="profileImage">
so i have the src coming from user object and i am changing the image and the user model is updated , but the url remains the same, although the content changes.
my Update function
this.appService.uploadProfileImage(this.user._id, this.imageFormData).then((response: any) => {
this.user = response;
});
so
the user.photo_url="somr-url" remains the same , but the image on the path changes.
But its not reflecting on the UI.
So how to reflect the changes on the img when the src is updated.
Things i tried but didnt worked.
Two way binding(as shown)
ChangeDetectorRef ( detectChange function)
try this
<img src="{{user.photo_url}}" alt="profileImage">
I just tested your code, you need to use [src] instead of [(src)].
I made a small demo in stackblitz & it works just fine!
<button (click)="changeImg()">change image source</button>
<br>
<img [src]="img" alt="profileImage">
ts code:
state : boolean;
img: string;
changeImg() {
this.state = ! this.state;
this.img = this.state ? "https://image.freepik.com/free-photo/blue-sky-with-clouds_1232-936.jpg" : "https://blogs.qub.ac.uk/queensuniversitybelfast/files/2015/05/red-sky.jpg"
}

React - Dynamic image rendering stutters when bundled/live

I have an issue with a component that takes a name property for use as an avatar (replaces spaces with dashes and adds file format). This then gets used as the img src dynamically.
Works absolutely fine locally, no stuttering changing the image. When I bundle and deploy to surge.sh and change routes the image stutters. I expected this as each image needs to be downloaded initially but even after the image is cached, swapping the image is jerky. Any ideas? Or is there a better way to do this?
class Athlete extends Component {
constructor (props) {
super(props);
}
render () {
let name = this.props.name;
let imgName = name.replace(/\s+/g, '-').toLowerCase();
let imgSrc = '/img/athletes/' + imgName + '.jpg';
return (
<div className="winner__inner">
<img src={imgSrc} alt={name} className="winner__avatar"/>
<h3 className="winner__name">{name}</h3>
</div>
);
}
}
export default Athlete;
Link: http://boulder.surge.sh/ (desktop only - click one of the events)
GIF: https://i.gyazo.com/4dea5302d9671b4de0fadc2334f872da.gif
Cached images: https://i.imgur.com/xQLgIWq.png
Any help would be great, thanks!

source issue with my image element and React

Alright hey guys. Trying to get this clock functioning with the pictures. I have more than just the one but if I can get the one figured out I can handle the others. After much searching I found out you can't use string interpolation within an attribute but I kept it there to show I tried. The second one I think "should work" and it shows the variable i'm trying or pic0, pic1 and so on but it's a string not the actual module i'm importing at the top (in the actual app I have them all imported but cut them down for brevity on the question. Any assistance?? I'm bashing my head what is causing this issue
import React, { Component } from 'react';
import './App.css';
import './bootstrap-3.3.7-dist/css/bootstrap.min.css';
import pic0 from './0.png';
export default class Clock extends Component {
constructor(props) {
super(props);
this.incrementTime = this.incrementTime.bind(this);
this.dayInWords = this.dayInWords.bind(this);
this.state = {
clock: new Date(),
day: (new Date()).getDay(),
hours0: 0,
minutes0: 0,
seconds0: 0,
hours1: 0,
minutes1: 0,
seconds1: 0
}
}
componentWillMount() {
let intervalTimer = setInterval(this.incrementTime, 1000);
}
componentWillUnmount() {
clearInterval(this.state.intervalTimer);
}
incrementTime() {
this.setState(currentState => {
return {
clock: new Date(),
seconds1: ((new Date()).getSeconds())%10
};
});
}
render() {
return(
<div className='panel-body imgContainer'>
<img src={pic0} alt='the digit 0' />
<p>Know this one doesnt work but kept here to show I tried it</p>
<img src={`pic${this.state.seconds1}`} alt='seconds1' />
<p>I think this should work and the src is what I want as the variable that im importing at the top but its just a string not the variable.</p>
<img src={'pic' + this.state.seconds1} alt='seconds1' />
<p>This is the end goal im trying to accomplish but im trying to see if it can be done using this.state.seconds so its kind of "dynamic" and the source changes every second thus changing the image</p>
<img src={pic0} alt='seconds1' />
</div>
);
}
}
this is what i'm seeing it's the image not showing the src is the "variable" im importing at the top but it's not actually calling that variable/module sorry if my terminology is wrong i'm clearly pretty new at this.
Thanks for all the assistance!!!!
Although this was clarified in the comments, for future visitors of this post - you need to reference the imported image directly:
import pic from './myImage';
// works
<img src={ pic } />
It can be placed in the state, or some other variable, but can't be accessed via a string with the same name:
// Doesn't work
<img pic={ 'pic' } />

Why can't I get the DOMNode's style (scrollHeight) in the React componentDidMount?

I am developping a React Application and trying to get the computed style 'scrollHeight' of a DOM Element.
I put this code in the componentDidMount:
componentDidMount() {
// let _this = this;
// window.onload = function(){
let imgFigureDOM = findDOMNode(_this.refs.imgFigure0),
imgW = imgFigureDOM.scrollWidth,
imgH = imgFigureDOM.scrollHeight;
// }
}
But, I can't get the correct value of scrollHeight only in the chrome browser.It seems that the chrome is not enough fast to render the DOMNode completely when the findDOMNode execute.
The value is correct when I use window.onload as above, but Shouldn't the DOMNode was completely loaded when the componentDidMount execute?
Thank you for your patient answer!
componentDidMount() is called when your React component is rendered. React has rendered an <img> tag, that doesn't mean that the image is loaded.
Let's set up some basic definitions to distinguish rendered from loaded:
rendered: React has converted your virtual DOM elements (specified in the render method) into real DOM elements and attached them to the DOM.
loaded: Image data or other remote content has downloaded completely (or failed to download).
So just add the onLoad and onError event handlers to your React <img> tag and away you go.
image-events
Short Example:
import React from 'react';
class ImageWithStatusText extends React.Component {
constructor(props) {
super(props);
this.state = { imageStatus: null };
}
handleImageLoaded(e){
this.setState({ imageStatus: 'loaded' });
console.log(e.target.scrollHeight);
}
handleImageErrored() {
this.setState({ imageStatus: 'failed to load' });
}
render() {
return (
<div>
<img
src={this.props.imageUrl}
onLoad={this.handleImageLoaded.bind(this)}
onError={this.handleImageErrored.bind(this)}
/>
{this.state.imageStatus}
</div>
);
}
}
export default ImageWithStatusText;

Categories