Skip to content

Commit f4c6c84

Browse files
author
Daniel Schmidt
committed
Update the render prop to follow the render prop pattern.
1 parent 37399d2 commit f4c6c84

File tree

6 files changed

+74
-81
lines changed

6 files changed

+74
-81
lines changed

README.md

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ The **`<Observer />`** accepts the following props:
113113
| **threshold** | Number | 0 | false | Number between 0 and 1 indicating the the percentage that should be visible before triggering. Can also be an array of numbers, to create multiple trigger points. |
114114
| **triggerOnce** | Bool | false | false | Only trigger this method once |
115115
| **onChange** | Func | | false | Call this function whenever the in view state changes |
116-
| **render** | Func | | false | Use render method to only render content when inView |
116+
| **render** | Func | | false | Render prop boolean indicating inView state |
117117
| **innerRef** | Func | | false | Get a reference to the the inner DOM node |
118118

119119
## Example code
@@ -135,31 +135,16 @@ const Component = () => (
135135
export default Component
136136
```
137137

138-
### Render callback
138+
### Render prop
139139

140-
For simple use cases where you want to only render a component when it enters
141-
view, you can use the `render` prop.
142140

143141
```js
144142
import Observer from 'react-intersection-observer'
145143

146144
const Component = () => (
147145
<Observer
148-
style={{ height: 200, position: 'relative' }}
149-
render={() => (
150-
<div
151-
style={{
152-
position: 'absolute',
153-
top: 0,
154-
bottom: 0,
155-
}}
156-
>
157-
<p>
158-
{
159-
'Make sure that the Observer controls the height, so it does not change change when element is added.'
160-
}
161-
</p>
162-
</div>
146+
render={(inView) => (
147+
inView => <h2>{`Header inside viewport ${inView}.`}</h2>
163148
)}
164149
/>
165150
)

index.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ declare module 'react-intersection-observer' {
55
/** Children should be either a function or a node */
66
children?: React.ReactNode | ((inView: boolean) => React.ReactNode);
77

8+
/** Render prop boolean indicating inView state */
9+
render?(inView: boolean): React.ReactNode;
10+
11+
812
/**
913
* The `HTMLElement` that is used as the viewport for checking visibility of
1014
* the target.
@@ -49,9 +53,6 @@ declare module 'react-intersection-observer' {
4953
/** Call this function whenever the in view state changes */
5054
onChange?(inView: boolean|React.FormEvent<HTMLElement>): void;
5155

52-
/** Use `render` method to only render content when inView */
53-
render?(): React.ReactNode;
54-
5556
/** Get a reference to the the inner DOM node */
5657
innerRef?(element?: HTMLElement): void;
5758
}

src/index.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ type Props = {
99
triggerOnce: boolean,
1010
/** Children should be either a function or a node */
1111
children?: ((inView: boolean) => React.Node) | React.Node,
12+
/** Render prop boolean indicating inView state */
13+
render?: (inView: boolean) => React.Node,
1214
/** Number between 0 and 1 indicating the the percentage that should be visible before triggering. Can also be an array of numbers, to create multiple trigger points. */
1315
threshold?: number | Array<number>,
1416
/** The HTMLElement that is used as the viewport for checking visibility of the target. Defaults to the browser viewport if not specified or if null.*/
@@ -20,8 +22,6 @@ type Props = {
2022
rootId?: string,
2123
/** Call this function whenever the in view state changes */
2224
onChange?: (inView: boolean) => void,
23-
/** Use render method to only render content when inView */
24-
render?: () => React.Node,
2525
/** Get a reference to the the inner DOM node */
2626
innerRef?: Function,
2727
}
@@ -134,9 +134,9 @@ class Observer extends React.Component<Props, State> {
134134
ref: this.handleNode,
135135
},
136136
// If render is a function, use it to render content when in view
137-
inView && typeof render === 'function' ? render() : null,
138-
// // If children is a function, render it with the current inView status.
139-
// // Otherwise always render children. Assume onChange is being used outside, to control the the state of children.
137+
typeof render === 'function' ? render(inView) : null,
138+
// If children is a function, render it with the current inView status.
139+
// Otherwise always render children. Assume onChange is being used outside, to control the the state of children.
140140
typeof children === 'function' ? children(inView) : children,
141141
)
142142
}

stories/Observer.story.js

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,37 @@ storiesOf('Intersection Observer', module)
3434
<ScrollWrapper>
3535
<Observer onChange={action('Child Observer inview')}>
3636
{inView => (
37-
<Header>{`Header inside viewport: ${inView.toString()}`}</Header>
37+
<Header>Header inside viewport: ${inView.toString()}</Header>
3838
)}
3939
</Observer>
4040
</ScrollWrapper>
4141
))
42+
.add('Render prop', () => (
43+
<ScrollWrapper>
44+
<Observer
45+
onChange={action('Render Observer inview')}
46+
render={inView => (
47+
<Header>Header is inside viewport: {inView.toString()}</Header>
48+
)}
49+
/>
50+
</ScrollWrapper>
51+
))
52+
.add('Plain child', () => (
53+
<ScrollWrapper>
54+
<Observer onChange={action('Plain Observer inview')}>
55+
<Header>
56+
Plain children are always rendered. Use onChange to monitor state.
57+
</Header>
58+
</Observer>
59+
</ScrollWrapper>
60+
))
4261
.add('With threshold 100%', () => (
4362
<ScrollWrapper>
4463
<Observer threshold={1} onChange={action('Child Observer inview')}>
4564
{inView => (
46-
<Header
47-
>{`Header is fully inside the viewport: ${inView.toString()}`}</Header>
65+
<Header>
66+
Header is fully inside the viewport: ${inView.toString()}
67+
</Header>
4868
)}
4969
</Observer>
5070
</ScrollWrapper>
@@ -53,8 +73,9 @@ storiesOf('Intersection Observer', module)
5373
<ScrollWrapper>
5474
<Observer threshold={0.5} onChange={action('Child Observer inview')}>
5575
{inView => (
56-
<Header
57-
>{`Header is 50% inside the viewport: ${inView.toString()}`}</Header>
76+
<Header>
77+
Header is 50% inside the viewport: ${inView.toString()}
78+
</Header>
5879
)}
5980
</Observer>
6081
</ScrollWrapper>
@@ -66,8 +87,10 @@ storiesOf('Intersection Observer', module)
6687
onChange={action('Hit threshold trigger')}
6788
>
6889
{inView => (
69-
<Header
70-
>{`Header is inside threshold: ${inView.toString()} - onChange triggers multiple times.`}</Header>
90+
<Header>
91+
Header is inside threshold: ${inView.toString()} - onChange triggers
92+
multiple times.
93+
</Header>
7194
)}
7295
</Observer>
7396
</ScrollWrapper>
@@ -84,8 +107,9 @@ storiesOf('Intersection Observer', module)
84107
onChange={action('Child Observer inview')}
85108
>
86109
{inView => (
87-
<Header
88-
>{`Header is inside the root viewport: ${inView.toString()}`}</Header>
110+
<Header>
111+
Header is inside the root viewport: ${inView.toString()}
112+
</Header>
89113
)}
90114
</Observer>
91115
</ScrollWrapper>
@@ -104,8 +128,9 @@ storiesOf('Intersection Observer', module)
104128
onChange={action('Child Observer inview')}
105129
>
106130
{inView => (
107-
<Header
108-
>{`Header is inside the root viewport: ${inView.toString()}`}</Header>
131+
<Header>
132+
Header is inside the root viewport: ${inView.toString()}
133+
</Header>
109134
)}
110135
</Observer>
111136
</ScrollWrapper>
@@ -120,8 +145,9 @@ storiesOf('Intersection Observer', module)
120145
onChange={action('Child Observer inview')}
121146
>
122147
{inView => (
123-
<Header
124-
>{`Header was fully inside the viewport: ${inView.toString()}`}</Header>
148+
<Header>
149+
Header was fully inside the viewport: ${inView.toString()}
150+
</Header>
125151
)}
126152
</Observer>
127153
</ScrollWrapper>
@@ -130,48 +156,17 @@ storiesOf('Intersection Observer', module)
130156
<ScrollWrapper>
131157
<Observer threshold={1} onChange={action('Child Observer inview')}>
132158
{inView => (
133-
<Header
134-
>{`Header 1 is fully inside the viewport: ${inView.toString()}`}</Header>
159+
<Header>
160+
Header 1 is fully inside the viewport: ${inView.toString()}
161+
</Header>
135162
)}
136163
</Observer>
137164
<Observer threshold={1} onChange={action('Child Observer inview')}>
138165
{inView => (
139-
<Header
140-
>{`Header 2 is fully inside the viewport: ${inView.toString()}`}</Header>
166+
<Header>
167+
Header 2 is fully inside the viewport: ${inView.toString()}
168+
</Header>
141169
)}
142170
</Observer>
143171
</ScrollWrapper>
144172
))
145-
.add('Render method', () => (
146-
<ScrollWrapper>
147-
<Observer
148-
style={{ height: 200, position: 'relative' }}
149-
onChange={action('Render Observer inview')}
150-
render={() => (
151-
<div
152-
style={{
153-
position: 'absolute',
154-
top: 0,
155-
bottom: 0,
156-
left: 0,
157-
right: 0,
158-
}}
159-
>
160-
<Header style={{ minHeight: '0', height: '100%' }}>
161-
Header is only rendered once observer is in view. Make sure that
162-
the Observer controls the height, so it does not change.
163-
</Header>
164-
</div>
165-
)}
166-
/>
167-
</ScrollWrapper>
168-
))
169-
.add('Plain child', () => (
170-
<ScrollWrapper>
171-
<Observer onChange={action('Plain Observer inview')}>
172-
<Header>
173-
Plain children are always rendered. Use onChange to monitor state.
174-
</Header>
175-
</Observer>
176-
</ScrollWrapper>
177-
))

tests/Observer.test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,20 @@ it('Should render <Observer /> with children inview', () => {
3737

3838
it('Should not render <Observer /> render outside view', () => {
3939
const wrapper = shallow(
40-
<Observer className="observer" render={() => <div>Render method</div>} />,
40+
<Observer
41+
className="observer"
42+
render={inView => <div>Inview: {inView.toString()}</div>}
43+
/>,
4144
)
4245
expect(wrapper).toMatchSnapshot()
4346
})
4447

4548
it('Should render <Observer /> render when in view', () => {
4649
const wrapper = shallow(
47-
<Observer className="observer" render={() => <div>Render method</div>} />,
50+
<Observer
51+
className="observer"
52+
render={inView => <div>Inview: {inView.toString()}</div>}
53+
/>,
4854
)
4955
wrapper.setState({ inView: true })
5056

tests/__snapshots__/Observer.test.js.snap

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33
exports[`Should not render <Observer /> render outside view 1`] = `
44
<div
55
className="observer"
6-
/>
6+
>
7+
<div>
8+
Inview:
9+
false
10+
</div>
11+
</div>
712
`;
813

914
exports[`Should render <Observer /> render when in view 1`] = `
1015
<div
1116
className="observer"
1217
>
1318
<div>
14-
Render method
19+
Inview:
20+
true
1521
</div>
1622
</div>
1723
`;

0 commit comments

Comments
 (0)