4

I want to use Picturefill + React + React Router (Also using Webpack).


Context: There is no isomorphic architecture yet so the data is fetched after initial page load (Route change).

…Because of this the render method is being called twice.

  1. Once for default state
  2. Once for updated state (fetched data)

Code

render: function () {
    return (
        <picture>
            <!-- You get the idea -->

            <source srcSet={this.props.large} media="(min-width: 1024px)" />
            <source srcSet={this.props.medium} media="(min-width: 640px)" />
            <img srcSet={this.props.small} />
        </picture>
    );
}

Example 1:

<head>
    <!-- Recommended usage -->
    <script src="https://cdn.rawgit.com/scottjehl/picturefill/2.3.1/dist/picturefill.js"></script>
</head>
  • Works in Safari 8.0.8
  • Works in Chrome 45
  • Works in Firefox 40.0.3 (Only on refresh, not on resize)

Example 2:

// Use picturefill JavaScript API
componentDidUpdate() {
    picturefill();
}
  • Works in Safari 8.0.8 (Only on resize, not on page load)
  • Works in Chrome 45
  • Works in Firefox 40.0.3 (Only on refresh, not on resize)

More info/Alternatives?

chemoish
  • 1,210
  • 2
  • 13
  • 23

2 Answers2

4

I would recommend, that you use picturefill 3.0 RC1 in combination with the mutation plugin. This way you don't need to call picturefill();, everything is done automatically.

This will work in any browser.

alexander farkas
  • 13,754
  • 4
  • 40
  • 41
  • Works for all the browsers listed above, even on resize (Extra 400lines of code—Good enough for now). – chemoish Sep 24 '15 at 23:49
  • 1
    This is the best way to load picturefill: https://github.com/aFarkas/lazysizes/blob/gh-pages/no-src.html#L10-L19 – alexander farkas Sep 25 '15 at 06:41
  • Hi @alexanderfarkas ! Thanks for your answer, I wanted to ask some clarifications. I have a react component that displays the images and it is where I trying to use `picturefill` package. You said to use picturefill & mutation plugin and you simply linked two folders where there are some files. Could you please go a bit deeper, cause I don't understand how to implement your suggestion in my scenario. Thanks a lot. – Fed Apr 06 '16 at 10:18
  • @chemoish I dont meen to discourage the usage of picture fill. However, I wrote a simple implementation of picturefill that accomodates landscape portrait in a few ours. On top of that I am using tiny blured image preloading technique as used on medium. This was actually quite simple. – dewwwald Jul 20 '17 at 21:04
  • 1
    install `picturefill` as npm package and use `import 'picturefill'; import 'picturefill/dist/plugins/mutation/pf.mutation.min';` to include in your app. That's it work'd done. – Sandeep Oct 24 '17 at 10:59
2

This solution works with Webpack and achieves the same functionality as the Recommended Usage in Example 1 above.

It seems that picturefill() will be initialized during the first render (Without the src defined) and then skipped during the second render (With the src defined).

So… preventing the rendering of the picture element, up until you have the data, seems to work.

componentDidUpdate: function () {
    picturefill();
},

render: function () {
    return (
        <div>
            {(function () {
                // You get the idea…
                if (this.props.large === undefined) {
                    return '';
                }

                return (
                    <picture>
                        <!-- You get the idea… -->

                        <source srcSet={this.props.large} media="(min-width: 1024px)" />
                        <source srcSet={this.props.medium} media="(min-width: 640px)" />
                        <img srcSet={this.props.small} />
                    </picture>
                );
            }.bind(this)())}
        </div>
    );
}
  • Works in Safari 8.0.8
  • Works in Chrome 45
  • Works in Firefox 40.0.3 (Only on refresh, not on resize)

UPDATE: 4/28/16

Now using isomorphic rendering with React so this solution doesn't work for me.

  1. Cannot import 'picturefill'; due to dependency on window which is not available in node.
  2. picturefill();, see below, does not work within componentDidUpdate for Safari (9.0.3).
  3. <picture> strategy previously described did not work for me due to flash of wrong image (fowi).
/**
 * OLD
 */

export class MyComponent extends Component {
  componentDidMount() {
    // TODO: remove when updated https://github.com/scottjehl/picturefill/pull/556
    const picturefill = require('picturefill');

    picturefill();
  }
}

/**
 * NEW
 */

// TODO: remove when updated https://github.com/scottjehl/picturefill/pull/556
if (__CLIENT__) {
  require('picturefill');
  require('picturefill/dist/plugins/mutation/pf.mutation'); // not sure if this does anything
}

export class MyComponent extends Component {}

Picture has been updated to the following:

<picture>
  <source media="(min-width: 640px)" srcSet={'...'} />
  <source srcSet={'...'} />

  {/* SEE: http://scottjehl.github.io/picturefill/#fowi-safari */}
  <img alt={title} />
</picture>

For Safari, flash of content still exists... But now shows the alt text...

As for require('picturefill/dist/plugins/mutation/pf.mutation');:

This plugin automatically detects any DOM mutation and polyfills new or changed responsive images automatically. It also adds support for responsive images IDL attributes/properties. If you have a highly dynamic website or a SPA you probably want to use this plugin. (This plugin does not work with IE8.)

chemoish
  • 1,210
  • 2
  • 13
  • 23
  • you get the idea? Not sure I do. – Fed Mar 30 '16 at 13:42
  • 2
    @FedericoKCL, sorry I am a bad communicator. The selected answer should be the way to go (If you do not have an isomorphic setup). If you DO have an isomorphic setup, you will need to trigger `picturefill` within `componentDidUpdate` because it only gets called client side. The code sample above is trying to indicate that you don't render the picture tag until sources become available (This is why there is a `return '';`). This will only be the case if your fetching the image sources from an API—hope this makes sense. – chemoish Apr 01 '16 at 04:33
  • Hi there, thanks it is clearer now. Do I need to do anything else to connect picturefill package into my component? such as `import Picturefill from 'picturefill' ` . Does this.props.large/medium/small stand for an image or what? thanks again. – Fed Apr 06 '16 at 10:41
  • Also, what if instead I have an isomorphic setup? How shall I go about using picturefill? – Fed Apr 06 '16 at 10:49
  • @FedericoKCL, yes, `this.props.large`, etc. represent images sources. For server-side rendering… I am currently using `componentDidMount() { consts picturefill = require('picturefill'); picturefill(); }`—excuse the formatting. For more information, see https://github.com/scottjehl/picturefill/pull/556. – chemoish Apr 08 '16 at 20:36