Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React svg / png render is flickering on new URL

Tags:

I'm using latest create-react-app config and facing the issues when switching between different routes.

All my SVG's are included in sprite file. Logically, sprite.svg file should be cached on the first-page load.

But instead, every change of route (with react-router 4) loads this file which causes flickering. Content changes instantly, but images loads with 1s lag. Same for png included via import in jsx.

From what I cant see in the console, same files download over and over again.

Live demo at http://cabin.surge.sh/ (i.e. try to change between Pricing / About pages in the header section)

enter image description here

Update:

The way I include SVG image - is dumb component <SvgIcon name="checkmark" />

import React, { Component } from 'react';
import sprite from '../images/sprite.svg';

export default class SvgIcon extends Component {
  render(){
    const { name } = this.props;
    return(
      <svg className={"ico ico-" + name}>
        <use xlinkHref={sprite + "#ico-" + name}></use>
      </svg>
    )
  }
}

PNG images

<img src={require(`../images/${authorImage}.png`)} srcSet={require(`../images/${authorImage}@2x.png`)  + ' 2x'} alt=""/>
like image 446
Sergey Khmelevskoy Avatar asked Jun 02 '18 17:06

Sergey Khmelevskoy


2 Answers

The reason you get a 200 instead of 304 is that you have a service worker which will intercept the request and serve the same from the cache itself. Which itself is 200 response. If you disable the service worker then you will get a 304

Service worker returning 200

like image 95
Tarun Lalwani Avatar answered Sep 28 '22 16:09

Tarun Lalwani


If the problem is that the image is not fetched earlier and causing flickering on page change, try to use

componentDidMount() {
  const sprite = "../images/sprite.svg";
  const prefetchLink = document.createElement("link");

  prefetchLink.href = sprite;
  prefetchLink.rel = "prefetch";
  prefetchLink.as = "image";
  document.body.appendChild(prefetchLink);
}

This will hint the browser to fetch resources in the background (idle time) that might be needed later, and store them in the browser’s cache. Once a page has finished loading it begins downloading additional resources and if a user then clicks on a prefetched link, it will load the content instantly.

For a functional version:

//custom hook to preload image
const usePreload = (url) => {
  const [loaded, setLoaded] = React.useState(false);
  const onLoad = React.useCallback(() => {
    setLoaded(true);
  }, []);
  React.useEffect(() => {
    const prefetchLink = document.createElement('link');
    prefetchLink.href = url;
    prefetchLink.rel = 'prefetch';
    prefetchLink.as = 'image';
    prefetchLink.addEventListener('load', onLoad);
    document.body.appendChild(prefetchLink);
    //clean up
    return () => document.body.removeChild(prefetchLink);
  }, [onLoad, url]);
  return loaded;
};

export default function SvgIcon({ name }) {
  const url = sprite + '#ico-' + name;
  const loaded = usePreload(url);
  return (
    loaded && ( //only render if image is loaded
      <svg className={'ico ico-' + name}>
        <use xlinkHref={url}></use>
      </svg>
    )
  );
}

Note that if you do server side rendering you don't want this code to execute on the server because there is no document on the server.

like image 25
Vikas Avatar answered Sep 28 '22 18:09

Vikas