Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 7fc0de7

Browse files
committed
let PopperJS perform DOM style updates
1 parent afb081a commit 7fc0de7

File tree

6 files changed

+58
-135
lines changed

6 files changed

+58
-135
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Upgrade PopperJS dependency to `1.12.9`
44

55
Fix `Popper` ref getting called too many times [#81](https://github.com/souporserious/react-popper/issues/81)
66

7+
Let PopperJS style DOM for better performance as described in [vjeux's talk](https://speakerdeck.com/vjeux/react-rally-animated-react-performance-toolbox).
8+
79
### 0.7.5
810
Fix PopperJS instantiation [#77](https://github.com/souporserious/react-popper/pull/77)
911

example/animated.jsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import React, { PureComponent } from 'react'
22
import ReactDOM, { findDOMNode } from 'react-dom'
3-
import Transition from 'react-motion-ui-pack'
3+
import Transition from 'react-transition-group/Transition';
44
import { Manager, Target, Popper, Arrow } from '../src/react-popper'
55
import outy from 'outy'
66

7+
const duration = 300
8+
9+
const defaultStyle = {
10+
transition: `opacity ${duration}ms ease-in-out`,
11+
opacity: 0,
12+
}
13+
14+
const transitionStyles = {
15+
entering: { opacity: 0 },
16+
entered: { opacity: 1 },
17+
}
18+
719
const CustomTarget = ({ innerRef, ...props }) => (
820
<button
921
ref={innerRef}
@@ -99,19 +111,19 @@ class AnimatedExample extends PureComponent {
99111
>
100112
Click {this.state.isOpen ? 'outside to hide' : 'to show'} popper
101113
</Target>
102-
<Transition
103-
component={false}
104-
enter={{ opacity: 1, scale: 1 }}
105-
leave={{ opacity: 0, scale: 0.9 }}
106-
>
107-
{this.state.isOpen && (
114+
<Transition in={this.state.isOpen} timeout={duration}>
115+
{state => (
108116
<Popper
109117
key="popper"
110118
component={CustomPopper}
111119
innerRef={c => {
112120
this.popper = findDOMNode(c)
113121
}}
114122
placement="bottom"
123+
style={{
124+
...defaultStyle,
125+
...transitionStyles[state]
126+
}}
115127
>
116128
<div>Animated Popper 🎉</div>
117129
</Popper>

example/index.jsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
1-
import React, { Component, PureComponent, Children, createElement } from 'react'
2-
import ReactDOM, { findDOMNode } from 'react-dom'
3-
import PropTypes from 'prop-types'
4-
import { VelocityTransitionGroup } from 'velocity-react'
5-
import Transition from 'react-motion-ui-pack'
6-
import { Manager, Target, Popper, Arrow } from '../src/react-popper'
7-
import PopperJS from 'popper.js'
8-
import Portal from 'react-travel'
9-
import outy from 'outy'
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
104
import MultipleExample from './multiple'
115
import AnimatedExample from './animated'
126
import ModifiersExample from './modifiers'
137

148
import './main.scss'
159

1610
const App = () => (
17-
<div
18-
style={{
19-
padding: 200,
20-
}}
21-
>
11+
<div style={{ padding: 200 }}>
2212
<div style={{ marginBottom: 200 }}>
2313
<MultipleExample />
2414
</div>
@@ -30,4 +20,5 @@ const App = () => (
3020
</div>
3121
</div>
3222
)
23+
3324
ReactDOM.render(<App />, document.getElementById('app'))

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-popper",
3-
"version": "0.7.5",
3+
"version": "0.8.0",
44
"description": "React wrapper around PopperJS.",
55
"main": "lib/react-popper.js",
66
"types": "react-popper.d.ts",
@@ -42,7 +42,8 @@
4242
},
4343
"dependencies": {
4444
"popper.js": "^1.12.9",
45-
"prop-types": "^15.5.10"
45+
"prop-types": "^15.5.10",
46+
"react-transition-group": "^2.2.1"
4647
},
4748
"devDependencies": {
4849
"babel-cli": "^6.16.0",
@@ -65,8 +66,6 @@
6566
"prettier": "^1.7.0",
6667
"react": "^15.6.1",
6768
"react-dom": "^15.6.1",
68-
"react-measure": "^2.0.2",
69-
"react-motion-ui-pack": "^0.10.3",
7069
"react-travel": "^1.3.0",
7170
"rimraf": "^2.6.2",
7271
"sass-loader": "^6.0.6",

src/Popper.jsx

Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,8 @@ class Popper extends Component {
4646
this._destroyPopper()
4747
this._createPopper()
4848
}
49-
5049
if (lastProps.children !== this.props.children) {
51-
this._popper.scheduleUpdate()
50+
this._scheduleUpdate()
5251
}
5352
}
5453

@@ -64,26 +63,14 @@ class Popper extends Component {
6463
return this.context.popperManager.getTargetNode()
6564
}
6665

67-
_getOffsets = data => {
68-
return Object.keys(data.offsets).map(key => data.offsets[key])
69-
}
70-
71-
_isDataDirty = data => {
72-
if (this.state.data) {
73-
return (
74-
JSON.stringify(this._getOffsets(this.state.data)) !==
75-
JSON.stringify(this._getOffsets(data))
76-
)
77-
} else {
78-
return true
79-
}
80-
}
81-
8266
_updateStateModifier = {
8367
enabled: true,
8468
order: 900,
8569
fn: data => {
86-
if (this._isDataDirty(data)) {
70+
if (
71+
data.placement !== this.state.placement ||
72+
data.hide !== this.state.hide
73+
) {
8774
this.setState({ data })
8875
}
8976
return data
@@ -94,25 +81,18 @@ class Popper extends Component {
9481
const { placement, eventsEnabled } = this.props
9582
const modifiers = {
9683
...this.props.modifiers,
97-
applyStyle: { enabled: false },
9884
updateState: this._updateStateModifier,
9985
}
100-
10186
if (this._arrowNode) {
10287
modifiers.arrow = {
10388
element: this._arrowNode,
10489
}
10590
}
106-
10791
this._popper = new PopperJS(this._getTargetNode(), this._node, {
10892
placement,
10993
eventsEnabled,
11094
modifiers,
11195
})
112-
113-
// schedule an update to make sure everything gets positioned correctly
114-
// after being instantiated
115-
this._popper.scheduleUpdate()
11696
}
11797

11898
_destroyPopper() {
@@ -121,27 +101,6 @@ class Popper extends Component {
121101
}
122102
}
123103

124-
_getPopperStyle = () => {
125-
const { data } = this.state
126-
127-
// If Popper isn't instantiated, hide the popperElement
128-
// to avoid flash of unstyled content
129-
if (!data) {
130-
return {
131-
position: 'absolute',
132-
pointerEvents: 'none',
133-
opacity: 0,
134-
}
135-
}
136-
137-
const { top, left, position } = data.offsets.popper
138-
139-
return {
140-
position,
141-
...data.styles,
142-
}
143-
}
144-
145104
_getPopperPlacement = () => {
146105
return this.state.data ? this.state.data.placement : undefined
147106
}
@@ -159,18 +118,22 @@ class Popper extends Component {
159118
}
160119
}
161120

162-
_getPopperRef = (node) => {
121+
_getPopperRef = node => {
163122
this._node = node
164123
if (node) {
165-
this._createPopper();
124+
this._createPopper()
166125
} else {
167-
this._destroyPopper();
126+
this._destroyPopper()
168127
}
169128
if (this.props.innerRef) {
170129
this.props.innerRef(node)
171130
}
172131
}
173132

133+
_scheduleUpdate = () => {
134+
this._popper && this._popper.scheduleUpdate()
135+
}
136+
174137
render() {
175138
const {
176139
component,
@@ -181,36 +144,24 @@ class Popper extends Component {
181144
children,
182145
...restProps
183146
} = this.props
184-
const popperStyle = this._getPopperStyle()
185147
const popperPlacement = this._getPopperPlacement()
186148
const popperHide = this._getPopperHide()
187149

188150
if (typeof children === 'function') {
189151
const popperProps = {
190152
ref: this._getPopperRef,
191-
style: popperStyle,
192153
['data-placement']: popperPlacement,
193154
['data-x-out-of-boundaries']: popperHide,
194155
}
195-
196156
return children({
197157
popperProps,
198158
restProps,
199-
scheduleUpdate: () => {
200-
// _createPopper will scheduleUpdate,
201-
// so calling this before this._popper exists
202-
// can be a noop.
203-
this._popper && this._popper.scheduleUpdate();
204-
},
159+
scheduleUpdate: this._scheduleUpdate,
205160
})
206161
}
207162

208163
const componentProps = {
209164
...restProps,
210-
style: {
211-
...restProps.style,
212-
...popperStyle,
213-
},
214165
'data-placement': popperPlacement,
215166
'data-x-out-of-boundaries': popperHide,
216167
}

yarn.lock

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,10 @@ clap@^1.0.9:
11961196
dependencies:
11971197
chalk "^1.1.3"
11981198

1199+
classnames@^2.2.5:
1200+
version "2.2.5"
1201+
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
1202+
11991203
cliui@^2.1.0:
12001204
version "2.1.0"
12011205
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
@@ -1409,7 +1413,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
14091413
safe-buffer "^5.0.1"
14101414
sha.js "^2.4.8"
14111415

1412-
create-react-class@^15.5.2, create-react-class@^15.6.0:
1416+
create-react-class@^15.6.0:
14131417
version "15.6.0"
14141418
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4"
14151419
dependencies:
@@ -2136,14 +2140,6 @@ get-caller-file@^1.0.1:
21362140
version "1.0.2"
21372141
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
21382142

2139-
get-node-dimensions@^1.2.0:
2140-
version "1.2.2"
2141-
resolved "https://registry.yarnpkg.com/get-node-dimensions/-/get-node-dimensions-1.2.2.tgz#7a71e8624cf9e1ab74599bb05b7e5116e995e45b"
2142-
2143-
get-prefix@^1.0.0:
2144-
version "1.0.0"
2145-
resolved "https://registry.yarnpkg.com/get-prefix/-/get-prefix-1.0.0.tgz#0d305448a4e3176f9c277175b14e16dbe6fba0b5"
2146-
21472143
get-stdin@^4.0.1:
21482144
version "4.0.1"
21492145
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -3403,10 +3399,6 @@ performance-now@^0.2.0:
34033399
version "0.2.0"
34043400
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
34053401

3406-
performance-now@^2.1.0:
3407-
version "2.1.0"
3408-
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
3409-
34103402
pify@^2.0.0, pify@^2.3.0:
34113403
version "2.3.0"
34123404
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -3834,12 +3826,6 @@ querystringify@~1.0.0:
38343826
version "1.0.0"
38353827
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
38363828

3837-
raf@^3.1.0:
3838-
version "3.3.2"
3839-
resolved "https://registry.yarnpkg.com/raf/-/raf-3.3.2.tgz#0c13be0b5b49b46f76d6669248d527cf2b02fe27"
3840-
dependencies:
3841-
performance-now "^2.1.0"
3842-
38433829
randomatic@^1.1.3:
38443830
version "1.1.7"
38453831
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
@@ -3875,31 +3861,6 @@ react-dom@^15.6.1:
38753861
object-assign "^4.1.0"
38763862
prop-types "^15.5.10"
38773863

3878-
react-measure@^2.0.2:
3879-
version "2.0.2"
3880-
resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.0.2.tgz#072a9a5fafc01dfbadc1fa5fb09fc351037f636c"
3881-
dependencies:
3882-
get-node-dimensions "^1.2.0"
3883-
prop-types "^15.5.10"
3884-
resize-observer-polyfill "^1.4.2"
3885-
3886-
react-motion-ui-pack@^0.10.3:
3887-
version "0.10.3"
3888-
resolved "https://registry.yarnpkg.com/react-motion-ui-pack/-/react-motion-ui-pack-0.10.3.tgz#b92c369c25d3897ea39d04a4c67eb6c57ced22e3"
3889-
dependencies:
3890-
get-prefix "^1.0.0"
3891-
prop-types "^15.5.10"
3892-
react-motion "^0.4.5"
3893-
3894-
react-motion@^0.4.5:
3895-
version "0.4.8"
3896-
resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.4.8.tgz#23bb2dd27c2d8e00d229e45572d105efcf40a35e"
3897-
dependencies:
3898-
create-react-class "^15.5.2"
3899-
performance-now "^0.2.0"
3900-
prop-types "^15.5.8"
3901-
raf "^3.1.0"
3902-
39033864
react-transition-group@^1.1.2:
39043865
version "1.2.0"
39053866
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.0.tgz#b51fc921b0c3835a7ef7c571c79fc82c73e9204f"
@@ -3910,6 +3871,17 @@ react-transition-group@^1.1.2:
39103871
prop-types "^15.5.6"
39113872
warning "^3.0.0"
39123873

3874+
react-transition-group@^2.2.1:
3875+
version "2.2.1"
3876+
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.1.tgz#e9fb677b79e6455fd391b03823afe84849df4a10"
3877+
dependencies:
3878+
chain-function "^1.0.0"
3879+
classnames "^2.2.5"
3880+
dom-helpers "^3.2.0"
3881+
loose-envify "^1.3.1"
3882+
prop-types "^15.5.8"
3883+
warning "^3.0.0"
3884+
39133885
react-travel@^1.3.0:
39143886
version "1.3.5"
39153887
resolved "https://registry.yarnpkg.com/react-travel/-/react-travel-1.3.5.tgz#4ff587550fd4d1a2e4371e5af48895d21d3c2898"
@@ -4120,10 +4092,6 @@ [email protected], [email protected]:
41204092
version "1.0.0"
41214093
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
41224094

4123-
resize-observer-polyfill@^1.4.2:
4124-
version "1.4.2"
4125-
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.4.2.tgz#a37198e6209e888acb1532a9968e06d38b6788e5"
4126-
41274095
right-align@^0.1.1:
41284096
version "0.1.3"
41294097
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"

0 commit comments

Comments
 (0)