Alternate image source in Javascript/React - javascript

I have an app that displays images from an API.
However some of the items don't have the required backdrop_path.
How would I display a different image if the original is not available
Here's my code
const MovieItem = ({ movie }) => {
const imagePath = 'https://image.tmdb.org/t/p/w500';
return (
<img src={`${imagePath}${movie.backdrop_path}`} alt={movie.title} />
I want the img to be {movie.poster_path} but only if {movie.backdrop_path} is null or not existent.
Or alternatively a hard coded image to display instead.

here's an answer for React, using a ref to allow the component to modify the image's source if it errors (i.e. the image specified by prop 'src' doesn't exist). In this example the fallback image is hard-coded, but could also be a prop etc.
import { useRef } from 'react';
const imageWithFallback = ({ src }) => {
const imgRef = useRef();
const onImageError = () => imgRef.current.src="/fallback-image.png";
return (
<img ref={imgRef} src={src} onError={onImageError} />
)
}

you can try use img's onerror event, it will be fired when it failed to load the resource.

You can use conditionally paths like this:
const MovieItem = ({ movie }) => {
const imagePath = 'https://image.tmdb.org/t/p/w500';
const src = ${movie.backdrop_path} === null || ${movie.backdrop_path} ===
undefined ? {movie.poster_path} : ${movie.backdrop_path}
return <img src={src} alt={movie.title} />
}

<img src={`${imagePath}${movie.backdrop_path || movie.poster_path}`} alt={movie.title} />
You can use conditional statements for images.

Related

I am trying to display my image after storing in state after I liked the image used a button to display the image

import { useState } from "react";
function Image({ image }) {
const [favourite, setFavourite] = useState([]);
const storeFavourites = () => {
setFavourite((gif) => [...gif, image]);
console.log(favourite);
};
const viewFavourites = () => {
favourite.map((url) => {
<img src={url} />;
});
};
return (
<div>
<img src={image} />
<button onClick={storeFavourites}>Like</button>
<button onClick={viewFavourites}>View Favourites</button>
</div>
);
}
export default Image;
This is a giphy site which generates a random gif when loaded and will generate a different gif based on search results. There is a like button to like the image and it was able to store the image as favourites in a state under const [favourite, setFavourite], however I was unable to display any of the favourite images when I clicked on View Favourites.
Looks like you are returning the view from the function which is not used anywhere inside the component's return function, which is the reason why nothing is rendered.
Here's something what you could do:
Store the favourite images in a list.
Display the image when a state value is toggled.
So, Here's what you could change:
import { useState } from "react";
function Image({ image }) {
const [favourite, setFavourite] = useState([]);
const [showFavourites, setShowFavourites] = useState(false);
const storeFavourites = () => {
setFavourite((gif) => [...gif, image]);
console.log(favourite);
};
const viewFavourites = () => {
setShowFavourites(!showFavourites);
};
return (
<div>
<img src={image} />
<button onClick={storeFavourites}>Like</button>
<button onClick={viewFavourites}>View Favourites</button>
{showFavourites && favourite.map((url, index) => {
<img src={url} key={index}
})}
</div>
);
}
export default Image;
If you don't wish to toggle the view but instead just it to change just one time, you could change the viewFavourites to this:
const viewFavourites = () => {
setShowFavourites(true);
};

Images are disappearing after refreshing the page in React

I am trying to display the data and image using a component.First time the data and images appears but when i refresh the page then data and images both disappear.
This is by component Team.js
import React from 'react';
const Team = (props)=>{
return(
<>
<h1>{props.data.name}</h1>
<img name="photo" src={require(`../images/${props.data.image}`)}/>
</>
)
}
export default Team;
My component is present in components folder and images are present in images folder.
require usually does not work with string literals (template strings). In other words, the location needs to be known at compile time.
I see two solutions.
1. Store your images in the public/images folder, and reference them using your website URL (Preferable)
Lets say you store all your image in the public/images folder. We can get the public url of the website using
var imageBasePath = window.location.protocol + "//" + window.location.host + "/images/";
this will then allow us to use this to reference our public images in the src for an img tag.
<img name="photo" src={imageBasePath + props.data.image} />
where image is the actual name of the image located in the public/images folder.
your team component would look like this
const Team = (props) => {
var imageBasePath =
window.location.protocol + "//" + window.location.host + "/images/";
return (
<>
<h1>{props.data.name}</h1>
<img name="photo" src={imageBasePath + props.data.image} />
</>
);
};
2. Store required images in an array and reference by index, export as an object.
Probably not the preferable method, as working with indexes can be tricky.
export const imageList = [
require("./checklist.jpg"),
require("./question.jpg"),
require("./test-pattern.jpg")
];
and then the Team implementation
import { imageList } from "./Images/imageList";
export const TeamRequire = (props) => {
let image = imageList[props.data.id];
return (
<>
<h1>{props.data.name}</h1>
<img name="photo" src={image} />
</>
);
};
to ease the index issue, we can store and fetch them by objectKey instead
export const imageListObj = {
checkList: require("./checklist.jpg"),
question: require("./question.jpg"),
testPattern: require("./test-pattern.jpg")
};
import { imageListObj } from "./Images/imageList";
export const TeamRequireObj = (props) => {
let image = imageListObj[props.data.imageId];
return (
<>
<h1>{props.data.name}</h1>
<img name="photo" src={image} />
</>
);
};
Here is a codesandbox with the three concepts.
https://codesandbox.io/s/adoring-rosalind-2xhj67?file=/src/App.js

Passing props via spread operator in React

Update: The plot has greatly thickened on this component as #wawka dove deep into the documentation and found that this should work but there are some wonky setups within the react-router-dom v^5.0.1. I'm still working through it but, this looks like it might require a rewrite of the myLink2 component.
Using React I have a component that I need to pass an 'id' prop to render an id on the html anchor. Starting at the lowest return level for this anchor point and working up we have:
// links.jsx
export const MyLink = ({children, location, ...props}) => {
const href = "mydomain.com" + location;
return (
<a href={href} {...props}>{children}</a>
)
}
export const MyLink2 = ({children, location, ...props}) => {
return (
<RouterLink to={location} {...props}>{children}</RouterLink>
)
}
//components.jsx
export const Block = ({linkLocation, htmlId, children, externalLink: isExternalLink}) => {
const EditLink = isExternalLink ? MyLink : MyLink2;
return <div className="outer-div">
<div className="inner-div">
{children}
</div>
<EditLink location={editLocation} id={htmlId}>{translate('edit')}</EditLink>
</div>
}
export const Summary = ({info1, info2, info3}) => {
return <Block editLocation={'/edit/location/' + info2} htmlId={'i-am-id-' + info2}>
<div>{info1}</div>
<div>{info2}</div>
<div>{info3}</div>
</Block>
}
That htmlId is what I'm seeking to pass up to myLink to assign the anchor's id attribute yet on page render it doesn't appear. Is it because id's are protected/special? Do I need to assign the spread operator on props to the EditLink component? Am I missing a passing point somewhere? I'm especially confused because similar questions show the spread operator as being just the right thing to do what I'm seeking.
Guidance would be much appreciated!
By all the research, this should have worked. As it would not in my application the workaround was to use a third MyLink3. I set it to a barebones link render and pass the component to MyLink2.
Like so:
// links.jsx
export const MyLink = ({children, location, ...props}) => {
const href = "mydomain.com" + location;
return (
<a href={href} {...props}>{children}</a>
)
}
export const MyLink2 = ({children, location, ...props}) => {
return (
<RouterLink to={location} component={MyLink3} {...props}>{children}</RouterLink>
)
}
export const MyLink3 = ({children, location, ...props}) => {
return (
<a href={href} {...props}>{children}</a>
)
}

Dynamic loading of images in React JS

I am trying to dynamically get images from my images folder based on some information retrieved from the database. Gone through as many resources as I could but still unable to solve the problem. Here's my code:
import scimitar from "../../images/scimitar.png";
import defender from "../../images/defender.png";
import arrows from "../../images/arrows.png";
import cape from "../../images/cape.png";
import platebody from "../../images/platebody.png";
const ItemCard = ({ item }) => {
return (
<div>
<p key={item.id}>ID: {item.id}</p>
<p>Name: {item.name}</p>
<p>{item.examine}</p>
<p>
<Link to={`/items/${item.id}`}>{item.name}</Link>
</p>
<img src={require(item.name)} alt={item.examine} />
</div>
)
}
const ItemList = () => {
const [items, setItems] = useState(null);
const populateItems = async () => {
const data = await getItems();
setItems(data);
};
useEffect(() => populateItems(), []);
return (
<div>
{items &&
items.map((item, index) => (
<ItemCard item={item} key={index} />
))
}
</div>
)
}
It looks like there are a couple of issues going on. Using template literals like
<img src={`../../images/${item.name}.png`} alt={item.examine} />
won't work either. The reason why is src doesn't take in a path to picture, it looks at a url your website uses. You'll need to setup your React app to serve public images (e.g. make sure something like localhost:1337/images/schimitar.png works).
Only then can you reference it using
<img src={`/images/${item.name}.png` />
To serve static files in create-react-app check out this link. If you have another setup you'll need to use something like babel-plugin-file-loader to serve public assets.
Not sure why this worked but I placed the path of the image in a variable before passing it to the src path of the image tag.
const ItemCard = ({ item }) => {
const imageItem = `/images/${item.name}.png`;
return (
<div>
<p key={item.id}>ID: {item.id}</p>
<p>Name: {item.name}</p>
<p>{item.examine}</p>
<span>Quantity: {item.quantity}</span>
<p>
<Link to={`/items/${item.id}`}>{item.name}</Link>
</p>
<img src={imageItem} alt={item.examine} />
</div>
)
}
export default ItemCard;
<img src={item.name} alt={item.examine} />
Try the following code if you are trying to get the image from a static path.
import image1 from 'images/image1.png';
<img src={image1} alt="image1" />
If you are trying to dynamically add the image then try the following code,
const imgName = "image1.png"
return (
<div>
{ imgName && <img src={`images/${imgName}`} alt="imgName" /> }
</div>
)

How do you push a UI update to a javascript frontend?

So I'm building a simple react app that fetches a bunch of images and displays them as cards.
The intention is to show an info message until all the images have loaded, then removing the notice again.
const App = () => {
const [cardInfo, setCardInfo] = useContext(CardInfoContext)
useEffect(() => {
fetchData(setCardInfo)
}, [])
useEffect(() => {
const app = document.querySelector('.app')
for(const child of app.children){
app.removeChild(child)
}
const loadingNotice = document.createElement('h1')
loadingNotice.innerHTML = "Fetching data ..."
app.appendChild(loadingNotice) //<-- this never shows up
cardInfo.forEach( info => {
const img = document.createElement('img')
img.src = info.image
app.appendChild(img)
})
app.removeChild(loadingNotice)
}, [cardInfo])
return (
<>
<div className="app">
<h1>Fetching data...</h1>
</div>
</>
)};
What instead happens is the app stays blank until all the images are loaded, then shows all the images at once -- but never the loading notice.
Can I somehow "push" the loading indicator change to the UI independent of the rest of the rendering?
Another thing I tried was
const App = () => {
const [cardInfo, setCardInfo] = useContext(CardInfoContext)
useEffect(() => {
fetchData(setCardInfo)
}, [])
useEffect(() => {
const app = document.querySelector('.app')
if(!cardInfo) return
const loadingNotice = app.querySelector(".loadingNotice")
loadingNotice.style.display = 'block' //<-- this never shows up
cardInfo.forEach( info => {
const img = document.createElement('img')
img.src = info.image
app.appendChild(img)
})
loadingNotice.style.display = 'none'
}, [cardInfo])
return (
<>
<div className="app">
<h1 className="loadingNotice">Fetching data...</h1>
</div>
</>
)}
Which would be incorrect because I do need to remove all the images, at least, but even that only displayed the loading notice for a fraction of a second, then the component goes blank until all the images can be displayed.
useEffect observes when cardInfo is changed, not when the render the comes after fired. You can use useLayoutEffect instead.
...but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
BTW, I wouldn't combine direct DOM manipulation with React to avoid issues like this (among other reasons)
Something like
const App = () => {
const [isLoadgin, setIsLoading] = useState(true)
const [cardInfo, setCardInfo] = useContext(CardInfoContext)
useEffect(() => {
fetchData(result => {
setCardInfo(result);
setIsLoading(false);
})
}, [])
return (
<>
<div className="app">
{isLoading && <h1 className="loadingNotice">Fetching data...</h1>}
{
cardInfo.map(card => <img src={card.image} />)
}
</div>
</>
)}
You need conditional rendering instead of all that DOM manipulation you are trying in the useEffect.
...
return(
<>
<div className="app">
{ !cardInfo ? <h1 className="loadingNotice">Fetching data...</h1> : <Cards info={cardInfo} /> }
</div>
</>
)
Note: I am assuming you have something like <Cards> component that displays cards details.

Categories