Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Import image dynamically in React component

I have an Articles component that shows a blog page with listed articles.

render() {
    const articles = {
        ...this.state.articles
    }

    const article = Object.keys(articles).map(cur => {
        return <Article
            key={this.state.articles[cur].id}
            imgName={this.state.articles[cur].thumb}
            title={this.state.articles[cur].title}
            meta={this.state.articles[cur].meta}
            clicked={() => this.detailedHandler(this.state.articles[cur].id)}
            detailed={this.state.articles[cur].detailed} />
    });

As you can see I pass image name with props to Article component. I want then to display the appropriate image for each article.

How do I import an image in Article component based on the props I receive (props.imgName) from Articles component?

like image 960
Dito Avatar asked Dec 14 '18 08:12

Dito


3 Answers

For anyone looking for a modern approach using async-await and custom react hooks, I found a pretty slick solution. Create a file called useImage.js and paste the following code:

import { useEffect, useState } from 'react'

const useImage = (fileName) => {
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState(null)
    const [image, setImage] = useState(null)

    useEffect(() => {
        const fetchImage = async () => {
            try {
                const response = await import(`../assets/img/${fileName}`) // change relative path to suit your needs
                setImage(response.default)
            } catch (err) {
                setError(err)
            } finally {
                setLoading(false)
            }
        }

        fetchImage()
    }, [fileName])

    return {
        loading,
        error,
        image,
    }
}

export default useImage

Then just import the custom hook into your image component, mine looks something like this:

import useImage from '../../hooks/useImage'

import Typography from './Typography' // simple plain-text react component

const Image = ({ fileName, alt, className, ...rest }) => {
    const { loading, error, image } = useImage(fileName)

    if (error) return <Typography>{alt}</Typography>

    return (
        <>
            {loading ? (
                <Typography>loading</Typography>
            ) : (
                <img
                    className={`Image${
                        className
                            ? className.padStart(className.length + 1)
                            : ''
                    }`}
                    src={image}
                    alt={alt}
                    {...rest}
                />
            )}
        </>
    )
}

export default Image

The nice thing about this solution is that no matter where your component is in relation to your assets folder, the react hook is always in the same place, so the relative path stays the same.

like image 81
Jaden Rose Avatar answered Sep 21 '22 15:09

Jaden Rose


I used context.

const images = require.context('../../../assets/img', true);
loadImage = imageName => (assets(`./${imageName}`).default);
<img src={loadImage("someimage.png")} alt="" />

I don't know if this is an optimal solution, but it works.

like image 21
Dito Avatar answered Oct 17 '22 05:10

Dito


You can load images dynamically from the API response with dynamic imports that is Stage 3 proposal as of now.

The resulting code should look something like:

loadImage = imageName => {
  import(`./assets/${imageName}.jpg`).then(image => {
    this.setState({
      image
    });
  });
};
render() {
  const { image } = this.state;
  return (
    <Fragment>
      {image && <img src={image} alt="" />}
    </Fragment>
  );
}

View Codesandbox Demo

This feature is supported out of the box in create-react-app, If using other systems, you can use the Babel plugin

like image 17
Agney Avatar answered Oct 17 '22 06:10

Agney