React img onError not firing - javascript

I am trying to use a third-party API for getting YouTube thumbnails with higher resolution, which sometimes fails with code 404. If I fail to fetch the image, I would like to replace the src with the default YouTube thumbnail retrieved using its own API (I have the url stored in a json). When I tried to implement this using the img onError event, it appears that nothing is fired when the fetch fails. Does anybody know what's going on? Thanks!
const TestView = () => {
const videoId = "tuZty35Fk7M"
return (
<div>
<img src={`https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`} onError={() => {console.log("errored")}}></img>
</div>
)
}
Update:
This ended up working pretty well
const TestView = () => {
const videoId = "tuZty35Fk7M"
const imgRef = useRef(null)
const [thumbnailSrc, setThumbnailSrc] = useState(`https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`)
const defaultThumbnailSrc = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`
const handleImgValidity = () => {
if (imgRef.current.naturalHeight == 90) {
setThumbnailSrc(defaultThumbnailSrc)
}
}
return (
<div>
<img ref={imgRef} src={thumbnailSrc} onLoad={handleImgValidity}></img>
</div>
)
}

It's nothing to do with React. Try visiting that url in your browser with a random string for the videoId. It will still display an image - in this case the default youtube thumbnail. So even though it's technically a 404, and your browser will report it as such, the image still has something to display so it won't fire the onError function.
If you replace the url in your code with something that is definitely a 404, your code works fine. Run the snippet below where i've swapped out youtube for google and you'll see your error message.
function onError() {
console.log('errored')
}
<img src="https://img.google.com/vi/some-random-id/maxresdefault.jpg" onError="onError()" />

Here is the working example of firing onError function.
https://codesandbox.io/s/reactjs-onerror-f7dq77

Related

Check the url path is valid in react

this is the dynamic url
../Assets/${product.code}/${image1}.png
Some product have images and some does not have one. Therefore if the URL is not valid, I want to return different JSX element. Is there any way to check the validity of the image URL path ?
You can do by try/catch if an error then returns a different JSX element.
setProductImageSrc() {
try{
const src = require(`../Assets/${product.code}/${image1}.png`)
this.setState({ src });
}
catch(err){
//Do whatever you want when the image failed to load here show return different JSX element
}
}
Solution
There is an attribute on the image tag called onError, which can triggered a function when the image fails to load. So what you can do is when image fails to load either put different image i.e default image or different JSX element as your wish.
Example:
import {useState} from "react"
function App() {
const[iserror,Setiserror]=useState(false)
const errorFunc =()=>{
Setiserror(true)
}
return (
<div>
{!iserror?<img onError={errorFunc} src="noimageurl" alt=""/>
:<div>This will show when image fails to load</div>
</div>
);
}
use fetch
const url="your-url-here"
fetch(url)
.then(res=>{
// valid image
})
.catch(error => {
// Image not found, Handle it here
}

draft-js-link-detection-plugin save to html with link?

So, what I'm trying to do is use this draftjs plugin to save text already formatted as HTML so I can insert it as HTML in my div to have clickable links, I already saw these examples and guides and tried to follow them, but I can't figure out what I'm doing wrong, examples and documentation used:
oficial documentation for plugin
Code sandBox with code example
-> These two I think were the really helpful ones and my base to write my code:
example of front with working implementation
The code behind that implementation
OBS: the problem is they don't really show how they save the formatted HTML state to pass in the div
After reading the documentation I coded this (dummies for show):
Some background to understand the code below
contentPost // useState passed in the <Editor>
const [contentPost, setContentPost] = useState(EditorState.createEmpty())
The component USAGE
<TextEditor
editorState={contentPost}
setEditorState={setContentPost}
hasError={hasError}
/>
component DEFINITION
function TextEditor({
editorState,
setEditorState
}: {
editorState: any
setEditorState: any
}) {
const linkifyPlugin = createLinkifyPlugin()
const plugins = [linkifyPlugin]
return (
<div>
<Editor
plugins={plugins}
editorState={editorState}
onEditorStateChange={setEditorState}
/>
</div>
)
}
How I save the values:
const payload = {
content: contentPost.getCurrentContent().getPlainText(),
contentFormatted: contentHtml,
}
handleCreatePost({ ...payload })
How I make the variable in which I save the HTML
const contentHtml = React.useMemo(() => {
const currentContent = contentPost.getCurrentContent()
return stateToHTML(currentContent)
}, [contentPost])
Any help would be appreciated!

OnLoad doesn't fire on cached images

I'd like to listen to the load event of my images. And here's how I'm trying to.
export const Picture: FC<PictureProps> = ({ src, imgCls, picCls, lazy, alt: initialAlt, onLoad, onClick, style }) => {
const alt = useMemo(() => initialAlt || generator.next().value, [src, initialAlt]);
const { publicRuntimeConfig } = getConfig();
return (
<picture style={style} className={picCls}>
{!publicRuntimeConfig.dev && <source srcSet={`${src}.webp`} type="image/webp"/>}
<img onLoad={onLoad} className={imgCls} alt={alt} src={src} loading={lazy ? 'lazy' : 'eager'}
onClick={onClick} />
</picture>
);
};
As I understand, this code should work fine. But when images are cached, they don't trigger load event from time to time.
I need to know, when all my images are loaded. To do this I checked the amount of loaded images. But it doesn't work when at least 1 of images didn't trigger the event. error event doesn't fire as well, and I can see that all of images are always loaded in the debugger tools.
What should I do?
I know I can add something like src?_dc=timestamp, but I do need caching, it really speeds up re-upload.
UPD: All of the loaded images returns 304 status. But the amount of images, that triggers load differs from 0 to total amount
Okay, this is not exactly what I asked for, but I found one thing that can help me - the completed field
export const Picture: FC<PictureProps> = ({ src, imgCls, picCls, lazy, alt: initialAlt, onLoad, onClick, style }) => {
const alt = useMemo(() => initialAlt || generator.next().value, [src, initialAlt]);
const ref = useRef<HTMLImageElement>();
const onLoadTriggered = useRef(false);
const { publicRuntimeConfig } = getConfig();
const onLoadFunc = useCallback(() => {
!onLoadTriggered.current && onLoad?.();
onLoadTriggered.current = true;
}, [onLoad]);
useEffect(() => {
ref.current.complete && onLoadFunc();
}, []);
return (
<picture style={style} className={picCls}>
{!publicRuntimeConfig.dev && <source srcSet={`${src}.webp`} type="image/webp"/>}
<img onLoad={onLoadFunc} className={imgCls} alt={alt} src={src} loading={lazy ? 'lazy' : 'eager'}
ref={ref} onClick={onClick} />
</picture>
);
};
This code did solve my problems, but it seems like it's a crutch. So, I've decided to leave this solution here, but I'm still waiting for the correct answer
this issue occurs because always it fetch images from cache
please add timestamp after image URL
you can do it from both side
From Backend (DB side)
for example :
#ImagePath+ REPLACE(ImagePath, '~', '') + '?time='+FORMAT(getdate(),'yyyyMMddHHmmssffff')
in IONIC code (Typescript)
for example :
npm i moment --save
import * as moment from moment
const dateStringThatWorksForIonicDatepicker = moment.unix(1536883200000).format(‘YYYY-MM-DD’)

React Hooks: useState with onClick only updating the SECOND time button is clicked?

Edit: forgot an important part - this is noticeable if you click the button next to Jeff A. Menges and check the console log.
The important part of the code is the "setFullResults(cardResults.data.concat(cardResultsPageTwo.data))" line in the onClick of the button code. I think it SHOULD set fullResults to whatever I tell it to... except it doesn't work the first time you click it. Every time after, it works, but not the first time. That's going to be trouble for the next set, because I can't map over an undefined array, and I don't want to tell users to just click on the button twice for the actual search results to come up.
I'm guessing useEffect would work, but I don't know how to write it or where to put it. It's clearly not working at the top of the App functional component, but anywhere else I try to put it gives me an error.
I've tried "this.forceUpdate()" which a lot of places recommend as a quick fix (but recommend against using - but I've been trying to figure this out for hours), but "this.forceUpdate()" isn't a function no matter where I put it.
Please help me get this button working the first time it's clicked on.
import React, { useState, useEffect } from "react";
const App = () => {
let artistData = require("./mass-artists.json");
const [showTheCards, setShowTheCards] = useState();
const [fullResults, setFullResults] = useState([]);
useEffect(() => {
setFullResults();
}, []);
let artistDataMap = artistData.map(artistName => {
//console.log(artistName);
return (
<aside className="artist-section">
<span>{artistName}</span>
<button
className="astbutton"
onClick={ function GetCardList() {
fetch(
`https://api.scryfall.com/cards/search?unique=prints&q=a:"${artistName}"`
)
.then(response => {
return response.json();
})
.then((cardResults) => {
console.log(cardResults.has_more)
if (cardResults.has_more === true) {
fetch (`https://api.scryfall.com/cards/search?unique=prints&q=a:"${artistName}"&page=2`)
.then((responsepagetwo) => {
return responsepagetwo.json();
})
.then(cardResultsPageTwo => {
console.log(`First Results Page: ${cardResults}`)
console.log(`Second Results Page: ${cardResultsPageTwo}`)
setFullResults(cardResults.data.concat(cardResultsPageTwo.data))
console.log(`Full Results: ${fullResults}`)
})
}
setShowTheCards(
cardResults.data
.filter(({ digital }) => digital === false)
.map(cardData => {
if (cardData.layout === "transform") {
return (
//TODO : Transform card code
<span>Transform Card (Needs special return)</span>
)
}
else if (cardData.layout === "double_faced_token") {
return (
//TODO: Double Faced Token card code
<span>Double Faced Token (Needs special return)</span>
)
}
else {
return (
<div className="card-object">
<span className="card-object-name">
{cardData.name}
</span>
<span className="card-object-set">
{cardData.set_name}
</span>
<img
className="card-object-img-sm"
alt={cardData.name}
src={cardData.image_uris.small}
/>
</div>
)
}
})
)
});
}}
>
Show Cards
</button>
</aside>
);
});
return (
<aside>
<aside className="artist-group">
{artistDataMap}
</aside>
<aside className="card-wrapper">
{showTheCards}
</aside>
</aside>
);
};
export default App;
CodesAndBox: https://codesandbox.io/embed/compassionate-satoshi-iq3nc?fontsize=14
You can try refactoring the code like for onClick handler have a synthetic event. Add this event Listener as part of a class. Use arrow function so that you need not bind this function handler inside the constructor. After fetching the data try to set the state to the result and use the state to render the HTML mark up inside render method. And when I run this code, I have also seen one error in console that child elements require key attribute. I have seen you are using Array.prototype.map inside render method, but when you return the span element inside that try to add a key attribute so that when React diffing algorithm encounters a new element it reduces the time complexity to check certain nodes with this key attribute.
useEffect(() => {
// call the functions which depend on fullResults here
setFullResults();
}, [fullResults])
// now it will check whether fullResults changed, if changed than call functions inside useEffect which are depending on fullResults

Cannot figure out how to play my record with React - Mic

I am working on a project for which the user have to record something and then he has the option to hear it.
I installed react-mic from npmjs.com :
https://www.npmjs.com/package/react-mic
In the above link they shared a demo:
https://hackingbeauty.github.io/react-mic/
The demo has the option to record, stop and eventually play your record.
I managed to implement the start and stop buttons, the recording seems to work fine but I do not understand two things:
Where is this record being saved?
After pressing the "Stop" button, there is a blobURL in the console. I assume that the record is saved to that blob, but how can I reach it? copy pasting the blobURL does not work.
My code looks exactly like their suggestion in npmjs.com
startRecording = () => {
this.setState({record: true});
}
stopRecording = () => {
this.setState({record: false});
}
onData(recordedBlob) {
console.log('chunk of real-time data is: ', recordedBlob);
}
onStop(recordedBlob) {
console.log('recordedBlob is: ', recordedBlob);
}
render(){
return(
<div>
<ReactMic
record = {this.state.record}
className = "sound-wave"
onStop = {this.onStop}
onData = {this.onData}
strokeColor="#009900"
backgroundColor="#FF8990"/><br/>
<button onClick = {this.startRecording}>start</button>
<button onClick = {this.stopRecording}>stop</button>
<MusicPlayer playlist = {SongChallenge}/> </div>
)
}
Thanks in advance.

Categories

Resources