Skip to content

Commit

Permalink
Updates imagesloaded to only have a single handler at any time. Allow…
Browse files Browse the repository at this point in the history
… end users to pass in options for imagesloaded
  • Loading branch information
Afram committed Mar 31, 2018
1 parent 23cc8cd commit e114a46
Show file tree
Hide file tree
Showing 6 changed files with 339 additions and 245 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
6.1.0
=====
#### New Features
- Adds ability to pass in `imagesloaded` options to be passed on by React Masonry Component

#### Bug Fixes
- Removes old imagesloaded listeners so there is only ever 1 active listener.
- Correctly cleans up reference to imagesloaded handlers when the component is unmounted

6.0.2
=====
- Allows gutter to be a number or string (Typescript)
Expand Down
142 changes: 72 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ if you wish to have IE8 support, v2 with React 0.14 is the highest version avail
1. [Basic usage](#basic-usage)
2. [Custom props](#custom-props)
3. [Accessing Masonry instance](#accessing-masonry-instance)
4. [Events](#events)
3. [Using with Webpack](#using-with-webpack)
1. [Dependencies](#dependencies)
2. [Webpack config \[DEPRECATED\]](#webpack-config)
4. [Images Loaded Options](#images-loaded-options)
5. [Events](#events)

#### Introduction:
A React.js Masonry component. (Also available as a [mixin](https://github.com/eiriklv/react-masonry-mixin) if needed)
Expand All @@ -33,39 +31,40 @@ A React.js Masonry component. (Also available as a [mixin](https://github.com/ei

##### Basic usage
``` npm install --save react-masonry-component```
```js
var React = require('react');
var Masonry = require('react-masonry-component');
```jsx
import * as React from 'react';
import Masonry from 'react-masonry-component';

var masonryOptions = {
const masonryOptions = {
transitionDuration: 0
};

var Gallery = React.createClass({
render: function () {
var childElements = this.props.elements.map(function(element){
class Gallery extends React.Component {
render() {
const childElements = this.props.elements.map(function(element){
return (
<li className="image-element-class">
<img src={element.src} />
</li>
);
});

return (
<Masonry
className={'my-gallery-class'} // default ''
elementType={'ul'} // default 'div'
options={masonryOptions} // default {}
disableImagesLoaded={false} // default false
updateOnEachImageLoad={false} // default false and works only if disableImagesLoaded is false
imagesLoadedOptions={imagesLoadedOptions} // default {}
>
{childElements}
</Masonry>
);
}
});
}

module.exports = Gallery;
export default Gallery;
```

ES6-style modules are also supported, just use:
Expand All @@ -77,22 +76,21 @@ import Masonry from 'react-masonry-component';
##### Custom props
You can also include your own custom props - EG: inline-style and event handlers.

```js
var React = require('react');
var Masonry = require('react-masonry-component');
```jsx
import * as React from 'react';
import Masonry from 'react-masonry-component';

var masonryOptions = {
const masonryOptions = {
transitionDuration: 0
};

var style = {
const style = {
backgroundColor: 'tomato'
};

var Gallery = React.createClass({
handleClick: function() { },

render: function () {
class Gallery extends React.Component {
handleClick() {}
render() {
return (
<Masonry
className={'my-gallery-class'}
Expand All @@ -103,32 +101,31 @@ var Gallery = React.createClass({
</Masonry>
);
}
});
}

module.exports = Gallery;
export default Gallery;
```

##### Accessing Masonry instance
Should you need to access the instance of Masonry (for example to listen to masonry events)
you can do so by using `refs`.

```js
var React = require('react');
var Masonry = require('react-masonry-component');

```jsx
import * as React from 'react';
import Masonry from 'react-masonry-component';

var Gallery = React.createClass({
handleLayoutComplete: function() { },
class Gallery extends React.Component {
handleLayoutComplete() { },

componentDidMount: function() {
componentDidMount() {
this.masonry.on('layoutComplete', this.handleLayoutComplete);
},

componentWillUnmount: function() {
componentWillUnmount() {
this.masonry.off('layoutComplete', this.handleLayoutComplete);
},

render: function () {
render() {
return (
<Masonry
ref={function(c) {this.masonry = this.masonry || c.masonry;}.bind(this)}
Expand All @@ -137,10 +134,42 @@ you can do so by using `refs`.
</Masonry>
);
}
});
}

module.exports = Gallery;
```
export default Gallery;
```

##### Images Loaded Options
React Masonry Component uses Desandro's `imagesloaded` library to detect when images have loaded. Should you want to pass
options down to it then you need to populate the `imagesLoadedOptions` property on React Masonry Component.

This will most commonly be used when the elements in your gallery have CSS background images and you want to capture their
load event. More info availabe on the [imagesloaded website](https://imagesloaded.desandro.com/#background).

eg:
```jsx
import * as React from 'react';
import Masonry from 'react-masonry-component';

class Gallery extends React.Component {
render() {
const imagesLoadedOptions = { background: '.my-bg-image-el' }

return (
<Masonry
className={'my-gallery-class'}
elementType={'ul'}
options={masonryOptions}
imagesLoadedOptions={imagesLoadedOptions}
>
<div className="my-bg-image-el"></div>
</Masonry>
);
}
}

export default Gallery;
```

##### Events

Expand All @@ -149,14 +178,14 @@ you can do so by using `refs`.
- `onRemoveComplete` - triggered after an item element has been removed

```jsx
var Gallery = React.createClass({
componentDidMount: function() {
class Gallery extends React.Component {
componentDidMount() {
this.hide();
},
handleImagesLoaded: function(imagesLoadedInstance) {
handleImagesLoaded(imagesLoadedInstance) {
this.show();
},
render: function(){
render() {
return (
<Masonry
onImagesLoaded={this.handleImagesLoaded}
Expand All @@ -167,32 +196,5 @@ var Gallery = React.createClass({
</Masonry>
)
}
});
```

#### Using with Webpack [Deprecated: this hack is no longer needed with masonry-layout 4.x]
Because webpack resolves AMD first, you need to use the imports-loader in order to disable AMD
and require as commonJS modules.

##### Dependencies
First ensure you have the imports-loader installed
```sh
npm install imports-loader --save
```

##### Webpack config
Then add the rules for the imports-loader to your webpack config.
The `babel-loader` is used below to show how you can use the 2 together.
```js
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
},
{
test: /masonry|imagesloaded|fizzy\-ui\-utils|desandro\-|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports?define=>false&this=>window'
}
]
```
}
```
27 changes: 16 additions & 11 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var propTypes = {
onImagesLoaded: PropTypes.func,
updateOnEachImageLoad: PropTypes.bool,
options: PropTypes.object,
imagesLoadedOptions: PropTypes.object,
elementType: PropTypes.string,
onLayoutComplete: PropTypes.func,
onRemoveComplete: PropTypes.func
Expand All @@ -25,7 +26,7 @@ var MasonryComponent = createReactClass({
erd: undefined,
latestKnownDomChildren: [],
displayName: 'MasonryComponent',
imagesLoadedCancellers: [],
imagesLoadedCancelRef: undefined,
propTypes: propTypes,

getDefaultProps: function() {
Expand All @@ -34,6 +35,7 @@ var MasonryComponent = createReactClass({
disableImagesLoaded: false,
updateOnEachImageLoad: false,
options: {},
imagesLoadedOptions: {},
className: '',
elementType: 'div',
onLayoutComplete: function() {
Expand Down Expand Up @@ -215,11 +217,20 @@ var MasonryComponent = createReactClass({
this.masonry.layout();
},

derefImagesLoaded: function() {
this.imagesLoadedCancelRef();
this.imagesLoadedCancelRef = undefined;
},

imagesLoaded: function() {
if (this.props.disableImagesLoaded) {
return;
}

if (this.imagesLoadedCancelRef) {
this.derefImagesLoaded();
}

var event = this.props.updateOnEachImageLoad ? 'progress' : 'always';
var handler = debounce(
function(instance) {
Expand All @@ -229,14 +240,12 @@ var MasonryComponent = createReactClass({
this.masonry.layout();
}.bind(this), 100);

var imgLoad = imagesloaded(this.masonryContainer).on(event, handler);
var imgLoad = imagesloaded(this.masonryContainer, this.props.imagesLoadedOptions).on(event, handler);

var canceller = function() {
handler.cancel();
this.imagesLoadedCancelRef = function() {
imgLoad.off(event, handler);
handler.cancel();
};

this.imagesLoadedCancellers.push(canceller);
},

initializeResizableChildren: function() {
Expand Down Expand Up @@ -264,7 +273,6 @@ var MasonryComponent = createReactClass({
},

componentDidMount: function() {
this.imagesLoadedCancellers = [];
this.initializeMasonry();
this.initializeResizableChildren();
this.imagesLoaded();
Expand All @@ -287,10 +295,7 @@ var MasonryComponent = createReactClass({
this.masonry.off('removeComplete', this.props.onRemoveComplete);
}

this.imagesLoadedCancellers.forEach(function(canceller) {
canceller();
});

this.derefImagesLoaded();

This comment has been minimized.

Copy link
@c10b10

c10b10 May 1, 2018

You need to add a check either for this.imagesLoadedCancelRef's existence or for !this.props.disableImagesLoaded before calling this, because the code fails when disableImagesLoaded is set to true and the component is getting unmounted.

this.masonry.destroy();
},

Expand Down
Loading

0 comments on commit e114a46

Please sign in to comment.