5

I created a component that renders images

this is the component.

import React, { lazy, Suspense } from "react";

const Icon = (props) => {
    const { src } = props;

    return (
        <img src={src}  />
    );
};

export default Icon;

then I use it like this

import ExampleIcon from "./../images/icons/example.png";
...
<Icon src={ExampleIcon} />

is there a more efficient way to load the icons? and then just "load" example.png and use it as a source? tried to change it to:

const Icon = (props) => {
    const src = lazy(() => import("./../images/icons/" + props.src + ".png"));
    return (
        <Suspense fallback={<p>loading...</p>}><img src={src} /></Suspense>
    );
};

looks like it doesn´t work that way. any other ideas? thanks!

handsome
  • 2,335
  • 7
  • 45
  • 73
  • 1
    I deleted my answer as it doesn't work that way as I look at the documentation. There's two things you can do to lazy load this. You can lazy load the COMPONENT (not the source of the img) or you can use native image loading by adding the `loading="lazy"` attribute. I'm not leaving this as an answer as I am unsure as to which direction you're trying to go and haven't actually tested this solution. – technicallynick Sep 12 '20 at 02:13
  • loading="lazy" is to load the image only if is in the viewport. what I´m trying to achieve is different. is dynamically load images. is a different thing. but thank you – handsome Sep 12 '20 at 16:21
  • You can load SVG inline, depending on your build tool. For many such tools, [this](https://stackoverflow.com/questions/46829592/react-returning-svg-contents-as-variable) is the thing to do. If not, that same link contains some somewhat manual alternatives, too. – loremdipso Oct 12 '20 at 16:17
  • What in particular isn't efficient enough about how things are handled normally via ``? Because it won't actually run a fetch for the image until the `img` is rendered. – Zachary Haber Oct 12 '20 at 17:43
  • `dynamically load images` - is very ambiguous statement. What is it that you want to do? If you want to lazy load the images, loading="lazy" might help you out and if you want to implement some sort of code splitting then Lazy and Suspense are the one to be used. @technicallynick's comment also makes a lot of sense. It will help if you can explain what is the end result you want. – Sunil Chaudhary Oct 18 '20 at 13:00

2 Answers2

2

No, you can't do this, since React.lazy() must be at the top level and only return React components. To lazily load images you can do inside an effect:

function Icon = props => {
    const [image, setImage] = useState()

    useEffect(() => {
        import("./../images/icons/" + props.src + ".png").then(setImage)
    }, [props.src])


    return image ? <img src={image} /> : 'Loading...'
}

Edit: there's one little problem with this, namely, Webpack will not be able to figure out the file to code split since the string in the import function is not a literal. You could still access files in the public directory dynamically using fetch instead. And perhaps you don't need fetch at all, just provide an url to src and spare the whole hassle.

Mordechai
  • 15,437
  • 2
  • 41
  • 82
1

You could apply this approach: Preloading images with JavaScript

const img=new Image();
img.src=url;

And how to do it with a hook with an online example:

https://www.selbekk.io/blog/2019/05/how-to-write-a-progressive-image-loading-hook/

Another approach just using hooks:

https://codesandbox.io/s/magical-pine-419kz?file=/src/App.tsx

import React, { useEffect } from "react";
import "./styles.css";

const loadImg = (src: string): Promise<string> =>
  new Promise((resolve, reject) => {
    const img = new Image();
    img.src = src;
    img.onload = () => resolve(src);
    img.onerror = () => reject(new Error("could not load image"));
  });

export default function App() {
  const [src, setSrc] = React.useState("preloadimg");
  useEffect(() => {
    const load = async () => {
      await loadImg(
        "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Chess_Large.JPG/800px-Chess_Large.JPG"
      ).then((src) => {
        setSrc(src);
      });
    }; // Execute the created function directly
    load();
  }, [src, setSrc]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <img src={src} alt="example" />
    </div>
  );
}
Steve Tomlin
  • 3,391
  • 3
  • 31
  • 63