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>
)
Related
I'm following this tutorial on YouTube https://youtu.be/b9eMGE7QtTk
The full code can be found here: https://gist.github.com/adrianhajdin/997a8cdf94234e889fa47be89a4759f1
The tutorial was great, but it didn't split all the functionalities into components which is React used for (or I'm so lead to believe).
So we have the App.js
import React, { useState, useEffect } from "react";
import MovieCard from "./MovieCard";
import SearchIcon from "./search.svg";
import "./App.css";
const API_URL = "http://www.omdbapi.com?apikey=b6003d8a";
const App = () => {
const [searchTerm, setSearchTerm] = useState("");
const [movies, setMovies] = useState([]);
useEffect(() => {
searchMovies("Batman");
}, []);
const searchMovies = async (title) => {
const response = await fetch(`${API_URL}&s=${title}`);
const data = await response.json();
setMovies(data.Search);
};
return (
<div className="app">
<h1>MovieLand</h1>
<div className="search">
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search for movies"
/>
<img
src={SearchIcon}
alt="search"
onClick={() => searchMovies(searchTerm)}
/>
</div>
{movies?.length > 0 ? (
<div className="container">
{movies.map((movie) => (
<MovieCard movie={movie} />
))}
</div>
) : (
<div className="empty">
<h2>No movies found</h2>
</div>
)}
</div>
);
};
export default App;
MovieCards.jsx is as follows:
import React from 'react';
const MovieCard = ({ movie: { imdbID, Year, Poster, Title, Type } }) => {
return (
<div className="movie" key={imdbID}>
<div>
<p>{Year}</p>
</div>
<div>
<img src={Poster !== "N/A" ? Poster : "https://via.placeholder.com/400"} alt={Title} />
</div>
<div>
<span>{Type}</span>
<h3>{Title}</h3>
</div>
</div>
);
}
export default MovieCard;
The app works, but I want to move className="search" to be its own component like Search /.
The code I end up having in App.js is
//at the top of App.jx
import Search from "./Search"
// in const App
<Search prop={searchMovies}/>
And in the new Seach / component
import { useState } from "react";
import SearchIcon from './search.svg';
const Search = ( prop ) => {
const [searchTerm, setSearchTerm] = useState("");
return (
<div className="search">
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
<img
src={SearchIcon}
alt="search"
onClick={() => prop(searchTerm)}
//props used to be searchMovies
/>
</div>
)
}
export default Search;
When typing something in the search field on the app and clicking on the search icon I get the following error:
prop is not a function
If my research has been correct, I need to use a constructor and super()
But it seems like the constructor needs to be called in a class Search instead of const Search as it breaks the code. Is that the case or is there a way to use the constructor in a function component, or is there something else completely that I should do?
Also, if there is a great tutorial you could recommend for super() I'd be really grateful.
Other thing that I want to do is to make a Results component or call it whatever that would have the {movies?.length > 0 ? ( part of the code, but I feel like that will be a different headache.
Basically what I want is to have:
const App = () => {
return (
<div className="app">
<h1>Movie Site</h1>
<Search />
<Results />
</div>
);
};
Or as shown in the picture
Hope all this makes sense. Also, I want to preface that I do not expect anyone to write the code for me, but if it helps me understand this it's appreciated. YT tutorials are appreciated as well.
Okay, after a push in the right direction from jonrsharpe and renaming the props into random things I figured it out.
As jonrsharpe said, my function is prop.prop, so if I wanted to call searchTerm in
onClick={() => prop(searchTerm)}
it should be
onClick={() => prop.prop(searchTerm)}
Now, that works, but looks silly. So renaming the first "prop" in prop.prop and the prop in const Search to searchOnClick leaves searchOnClick.prop(searchTerm) which still works. Great.
Then in App.js renaming prop in Search prop={searchMovies} to searchOnClick={searchMovies} needs to be followed by renaming searchOnClick.prop in Search.jsx to searchOnClick.searchOnClick.
Lastly, we want to destructure the props as jonrsharpe said.
const Search = ( searchOnClick ) => {
would become
const Search = ( {searchOnClick} ) => {
That allows us to remake searchOnClick.searchOnClick(searchTerm) to searchOnClick(searchTerm) only.
The whole point is that the prop calls the whole componentName variable=value but it doesn't take the value of the variable automatically so it needs to be called like prop.variable until destructured where it can be called as variable only.
Now that I figured this out it feels silly spending two days on this. Thanks to jonrsharpe again, and hope this helps to someone else in the future.
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
I have the following structure:
- blog (directory)
-- index.js (list of all blog articles)
-- [slug].js (single article)
when I am inside index.js I have:
const Blog = props => {
const { pageProps: { articles } } = props
const blogArticles = objectToArray(articles)
return (
<Layout bio={props.bio}>
<Title>
I am excited by the idea of sharing my knowledge and perhaps inspiring others.
</Title>
{blogArticles.map(
(article, i) => <Card key={`id-${i}`} data={article} />
)}
</Layout>
)
}
Blog.getInitialProps = async () => {
const articles = await getBlogArticles()
return { articles }
}
and the card component has the link:
...
<Link href={`/blog/${slug}`}>
<Wrapper>
<ImgWrapper>
<Img src={BASE_URL + url} />
</ImgWrapper>
<TextWrapper>
<Title>{title}</Title>
<ArtcilePreview>{intro}</ArtcilePreview>
</TextWrapper>
</Wrapper>
</Link>
...
and inside [slug].js I have:
const BlogArticle = (props) => {
return (
<Layout bio={props.bio}>
<Article title={props.pageProps.article[0].title} content={props.pageProps.article[0].content} />
<ArticleShare url={'process.env.API_URL + asPath'} />
</Layout>
)
}
BlogArticle.getInitialProps = async ({ query }) => {
const article = await getArticleBySlug(query.slug)
return { article }
}
when I click within the card component to go to the dynamically generated page, it works correctly. However, during the transition from url/blog to url/blog/my-slug I can see an error message appearing and disappearing quickly in the console.
It looks like it cannot find the page that is dynamically generated throwing what I think to be a 500 error.
I cannot figure out why it appears and disappears so quickly.
ERROR BELOW I HAVE RECORDED A SCREEN VIDEO
According to the nextjs documentation you should not add the actual slug in href
If you check the docs of Link I linked above correct usage is
<Link href="/blog/[slug]" as={`/blog/${slug}`}>
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.
I'm writing a react app, with mobx and framework7, and using code splitting in some of my imports, and I haven't found anything on this, but i want to know if there's a way for me to use a variable inside a import, something like this code:
const Slider = import("../parts/Slider").then( Slider =>
<Slider>
{
//sources would be an array of urls
sources.map( (source, i,) =>
<div className="slider" key={i} style={{position:'relative'}}>
<img className="picture" src={source} alt=""/>
</div>
)
}
</Slider>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.1/umd/react-dom.production.min.js"></script>
You should just use normal import and pass parameters to your React component. Why not to do something like:
import Slider from "../parts/Slider";
const SliderComp = ( {sources}, ) =>
<Slider>
{
//sources would be an array of urls
sources.map( (source, i,) =>
<div className="slider" key={i} style={{position:'relative'}}>
<img className="picture" src={source} alt=""/>
</div>
)
}
</Slider>
export default SliderComp;
and to use it in other file:
import Slider from './SliderComp'
...
<Slider sources={arrayOfSources} />