Skip to content

Commit 9b3ce8e

Browse files
author
fpapado
committed
Pass ref, use prop collection
1 parent b57da8a commit 9b3ce8e

7 files changed

+103
-20
lines changed

ROADMAP.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
- [x] Upgrade devDeps
99
- [x] Upgrade microbundle
10-
- [ ] Check --external all consequence
11-
- [ ] Upgrade react-intersection-observer
10+
- [x] Check --external all consequence
11+
- [x] Upgrade react-intersection-observer
12+
- [ ] Pass Ref
13+
- [ ] Prop getter
1214
- [ ] Split out renderXYZ components
1315
- [ ] Refactor props
1416
- [ ] Props getter

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"umd:main": "dist/react-lazy-images.umd.js",
99
"types": "dist/index.d.ts",
1010
"scripts": {
11-
"dev": "npm-run-all --parallel bundle:dev storybook",
11+
"dev": "npm-run-all bundle:dev storybook",
1212
"dev:ts": "tsc --watch --pretty",
1313
"build": "npm-run-all bundle:prod size",
1414
"bundle:dev": "microbundle --watch --compress false",

src/LazyImage.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,25 @@ export const LazyImage: React.StatelessComponent<LazyImageProps> = ({
5050
...rest
5151
}) => (
5252
<LazyImageFull {...rest}>
53-
{({ src, srcSet, alt, imageState }) => {
53+
{({ imageState, imageProps }) => {
5454
// Call the appropriate render callback based on the state
5555
// and the props specified, passing on relevant props.
5656
switch (imageState) {
5757
case ImageState.NotAsked:
58-
return !!placeholder && placeholder({ alt });
58+
return !!placeholder && placeholder(imageProps);
5959

6060
case ImageState.Loading:
6161
// Only render loading if specified, otherwise placeholder
62-
return !!loading ? loading() : !!placeholder && placeholder({ alt });
62+
return !!loading
63+
? loading()
64+
: !!placeholder && placeholder(imageProps);
6365

6466
case ImageState.LoadSuccess:
65-
return actual({ src, alt, srcSet });
67+
return actual(imageProps);
6668

6769
case ImageState.LoadError:
6870
// Only render error if specified, otherwise actual (broken image)
69-
return !!error ? error() : actual({ src, alt, srcSet });
71+
return !!error ? error() : actual(imageProps);
7072
}
7173
}}
7274
</LazyImageFull>

src/LazyImageFull.tsx

+14-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import React from "react";
2-
import Observer, {
3-
IntersectionObserverProps
4-
} from "react-intersection-observer";
2+
import Observer from "react-intersection-observer";
53

64
/**
75
* Valid props for LazyImage components
@@ -39,10 +37,14 @@ export interface LazyImageFullProps extends CommonLazyImageProps {
3937
/** Values that the render props take */
4038
export interface RenderCallbackArgs {
4139
imageState: ImageState;
42-
src?: string;
43-
srcSet?: string;
44-
alt?: string;
45-
sizes?: string;
40+
imageProps: {
41+
src?: string;
42+
srcSet?: string;
43+
alt?: string;
44+
sizes?: string;
45+
/** When not loading eagerly, a ref to bind to the DOM element. This is needed for the intersection calculation to work. */
46+
ref?: React.RefObject<{}>;
47+
};
4648
}
4749

4850
/** Subset of react-intersection-observer's props */
@@ -146,11 +148,11 @@ export class LazyImageFull extends React.Component<
146148

147149
// Render function
148150
render() {
149-
const { children, loadEagerly, observerProps, ...cbProps } = this.props;
151+
const { children, loadEagerly, observerProps, ...imageProps } = this.props;
150152

151153
if (loadEagerly) {
152154
// If eager, skip the observer and view changing stuff; resolve the imageState as loaded.
153-
return children({ imageState: ImageState.LoadSuccess, ...cbProps });
155+
return children({ imageState: ImageState.LoadSuccess, imageProps });
154156
} else {
155157
return (
156158
<Observer
@@ -160,7 +162,9 @@ export class LazyImageFull extends React.Component<
160162
onChange={this.onInView}
161163
triggerOnce
162164
>
163-
{children({ imageState: this.state.imageState, ...cbProps })}
165+
{({ ref }) =>
166+
children({ imageState: this.state.imageState, imageProps })
167+
}
164168
</Observer>
165169
);
166170
}

src/react-intersection-observer.d.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/* Custom react-interesction-observer types, that match upstream */
2+
declare module "react-intersection-observer" {
3+
import React = require("react");
4+
5+
export interface RenderCallbackArgs {
6+
inView: boolean;
7+
ref: React.RefObject<{}>;
8+
}
9+
10+
export interface IntersectionObserverProps {
11+
/** Children should be either a function or a node */
12+
children?:
13+
| React.ReactNode
14+
| ((args: RenderCallbackArgs) => React.ReactNode);
15+
16+
/** Render prop boolean indicating inView state */
17+
render?(inView: boolean): React.ReactNode;
18+
19+
/**
20+
* The `HTMLElement` that is used as the viewport for checking visibility of
21+
* the target.
22+
* Defaults to the browser viewport if not specified or if `null`.
23+
*/
24+
root?: HTMLElement | null;
25+
26+
/**
27+
* Unique identifier for the root element - This is used to identify the
28+
* `IntersectionObserver` instance, so it can be reused.
29+
* If you defined a root element, without adding an `id`, it will create a new
30+
* instance for all components.
31+
*/
32+
rootId?: string;
33+
34+
/**
35+
* Margin around the root.
36+
* Can have values similar to the CSS margin property,
37+
* e.g. `"10px 20px 30px 40px"` (top, right, bottom, left).
38+
*/
39+
rootMargin?: string;
40+
41+
/**
42+
* Element tag to use for the wrapping component
43+
* @default `'div'`
44+
*/
45+
tag?: string;
46+
47+
/** Number between 0 and 1 indicating the the percentage that should be
48+
* visible before triggering. Can also be an array of numbers, to create
49+
* multiple trigger points.
50+
* @default `0`
51+
*/
52+
threshold?: number | number[];
53+
54+
/**
55+
* Only trigger this method once
56+
* @default `false`
57+
*/
58+
triggerOnce?: boolean;
59+
60+
/** Call this function whenever the in view state changes */
61+
onChange?(inView: boolean | React.FormEvent<HTMLElement>): void;
62+
63+
/** Get a reference to the the inner DOM node */
64+
innerRef?(element?: HTMLElement): void;
65+
}
66+
67+
export default class IntersectionObserver extends React.Component<
68+
IntersectionObserverProps,
69+
{}
70+
> {}
71+
}

stories/utils.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22

3-
export const Container = ({ children }) => (
3+
export const Container: React.SFC<{}> = ({ children }) => (
44
<div className="pa3 near-black bg-washed-yellow">
55
<div
66
className="flex justify-center items-center"

tsconfig.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
"moduleResolution": "node",
1313
"allowSyntheticDefaultImports": true,
1414
"esModuleInterop": true,
15-
"lib": ["es6", "dom"]
15+
"lib": ["es6", "dom"],
16+
"baseUrl": "./",
17+
"paths": {
18+
"react-intersection-observer": ["src/react-intersection-observer.d.ts"]
19+
}
1620
},
1721
"include": ["src/**/*"],
1822
"exclude": ["node_modules", "dist"]

0 commit comments

Comments
 (0)