Skip to content

Commit d4c9e20

Browse files
Merge pull request #82 from valerybugakov/avoid-reflow-on-selectbox-updates
Avoid reflow on selectbox updates
2 parents c134f40 + 0edd981 commit d4c9e20

22 files changed

+1576
-1222
lines changed

.babelrc

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,5 @@
44
"@babel/typescript",
55
["@babel/preset-env", { "modules": false }]
66
],
7-
"plugins": ["@babel/plugin-proposal-class-properties"],
8-
"env": {
9-
"production": {
10-
"plugins": [
11-
[
12-
"transform-react-remove-prop-types",
13-
{
14-
"removeImport": true
15-
}
16-
]
17-
]
18-
}
19-
}
7+
"plugins": ["@babel/plugin-proposal-class-properties"]
208
}

.prettierrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"parser": "typescript",
33
"singleQuote": true,
44
"printWidth": 100,
5-
"semi": false
5+
"semi": false,
6+
"arrowParens": "avoid"
67
}

example/src/App.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import React, { createRef, Component } from 'react'
22

33
import { TAlbumItem } from './sample-data'
44
import { SelectableGroup } from '../../src'
5-
import Counters from './Counters'
6-
import List from './List'
5+
import { Counters } from './Counters'
6+
import { List } from './List'
77

88
type TAppProps = {
99
items: TAlbumItem[]
@@ -19,7 +19,7 @@ class App extends Component<TAppProps, TAppState> {
1919
state = {
2020
disableFirstRow: false,
2121
reversed: false,
22-
showSelectableGroup: true
22+
showSelectableGroup: true,
2323
}
2424

2525
countersRef = createRef<Counters>()
@@ -38,7 +38,7 @@ class App extends Component<TAppProps, TAppState> {
3838

3939
toggleSelectableGroup = () => {
4040
this.setState(state => ({
41-
showSelectableGroup: !state.showSelectableGroup
41+
showSelectableGroup: !state.showSelectableGroup,
4242
}))
4343
}
4444

@@ -47,10 +47,11 @@ class App extends Component<TAppProps, TAppState> {
4747
}
4848

4949
handleSelectionFinish = selectedItems => {
50+
console.log('Handle selection finish', selectedItems.length)
5051
this.countersRef.current.handleSelectionFinish(selectedItems)
5152
}
5253

53-
handleSelectedItemUnmount = (unmountedItem, selectedItems) => {
54+
handleSelectedItemUnmount = (_unmountedItem, selectedItems) => {
5455
console.log('hadneleSelectedItemUnmount')
5556
this.countersRef.current.handleSelectionFinish(selectedItems)
5657
}

example/src/Card.tsx

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,33 @@
1-
import React, { Component } from 'react'
1+
import React from 'react'
22

33
import { createSelectable, TSelectableItemProps } from '../../src'
4-
import Label from './Label'
4+
import { Label } from './Label'
55

6-
type TAlbumProps = TSelectableItemProps & {
6+
type TAlbumProps = {
77
player: string
88
year: number
99
}
1010

1111
const DISABLED_CARD_YEARS = [10, 22, 27, 54, 82, 105, 150]
1212

13-
class Card extends Component<TAlbumProps> {
14-
render() {
15-
const { selectableRef, isSelected, isSelecting, player, year } = this.props
13+
export const Card = createSelectable<TAlbumProps>((props: TSelectableItemProps & TAlbumProps) => {
14+
const { selectableRef, isSelected, isSelecting, player, year } = props
1615

17-
const classNames = [
18-
'item',
19-
DISABLED_CARD_YEARS.includes(year) && 'not-selectable',
20-
isSelecting && 'selecting',
21-
isSelected && 'selected'
22-
]
23-
.filter(Boolean)
24-
.join(' ')
16+
const classNames = [
17+
'item',
18+
DISABLED_CARD_YEARS.includes(year) && 'not-selectable',
19+
isSelecting && 'selecting',
20+
isSelected && 'selected',
21+
]
22+
.filter(Boolean)
23+
.join(' ')
2524

26-
return (
27-
<div ref={selectableRef} className={classNames}>
28-
<div className="tick">+</div>
29-
<h2>{player}</h2>
30-
<small>{year}</small>
31-
<Label isSelected={isSelected} isSelecting={isSelecting} />
32-
</div>
33-
)
34-
}
35-
}
36-
37-
export default createSelectable(Card)
25+
return (
26+
<div ref={selectableRef} className={classNames}>
27+
<div className="tick">+</div>
28+
<h2>{player}</h2>
29+
<small>{year}</small>
30+
<Label isSelected={isSelected} isSelecting={isSelecting} />
31+
</div>
32+
)
33+
})

example/src/Counters.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { Component } from 'react'
22

3-
class Counters extends Component {
3+
export class Counters extends Component {
44
state = {
55
selectedItems: [],
6-
selectingItems: []
6+
selectingItems: [],
77
}
88

99
handleSelecting = selectingItems => {
@@ -15,7 +15,7 @@ class Counters extends Component {
1515
handleSelectionFinish = selectedItems => {
1616
this.setState({
1717
selectedItems,
18-
selectingItems: []
18+
selectingItems: [],
1919
})
2020

2121
console.log(`Finished selection ${selectedItems.length}`)
@@ -34,5 +34,3 @@ class Counters extends Component {
3434
)
3535
}
3636
}
37-
38-
export default Counters

example/src/Label.tsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
1-
import React, { Component } from 'react'
1+
import React from 'react'
22

33
type TLabelProps = {
44
isSelecting: boolean
55
isSelected: boolean
66
}
77

8-
class Label extends Component<TLabelProps> {
9-
render() {
10-
const { isSelecting, isSelected } = this.props
8+
export function Label(props: TLabelProps) {
9+
const { isSelecting, isSelected } = props
1110

12-
return (
13-
<div className="card-label">
14-
Selecting: <span>{`${isSelecting}`}</span>
15-
<br />
16-
Selected: <span>{`${isSelected}`}</span>
17-
</div>
18-
)
19-
}
11+
return (
12+
<div className="card-label">
13+
Selecting: <span>{`${isSelecting}`}</span>
14+
<br />
15+
Selected: <span>{`${isSelected}`}</span>
16+
</div>
17+
)
2018
}
21-
22-
export default Label

example/src/List.tsx

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,32 @@
1-
import React, { Component } from 'react'
1+
import React, { memo } from 'react'
22

33
import { TAlbumItem } from './sample-data'
44
import { DeselectAll, SelectAll } from '../../src'
5-
import SelectableCard from './Card'
5+
import { Card } from './Card'
66

77
type TListProps = {
88
items: TAlbumItem[]
99
}
1010

11-
class List extends Component<TListProps> {
12-
shouldComponentUpdate(nextProps: TListProps) {
13-
return nextProps.items !== this.props.items
14-
}
11+
export const List = memo((props: TListProps) => {
12+
const { items } = props
1513

16-
render() {
17-
const { items } = this.props
18-
19-
return (
20-
<div>
21-
<p className="not-selectable">Press ESC to clear selection</p>
22-
<div className="button-container">
23-
<SelectAll component="button" type="button" className="btn">
24-
Select all
25-
</SelectAll>
26-
<DeselectAll component="button" type="button" className="btn">
27-
Clear selection
28-
</DeselectAll>
29-
</div>
30-
<div className="albums">
31-
{items.map(item => (
32-
<SelectableCard key={item.year} player={item.player} year={item.year} />
33-
))}
34-
</div>
14+
return (
15+
<div>
16+
<p className="not-selectable">Press ESC to clear selection</p>
17+
<div className="button-container">
18+
<SelectAll component="button" type="button" className="btn">
19+
Select all
20+
</SelectAll>
21+
<DeselectAll component="button" type="button" className="btn">
22+
Clear selection
23+
</DeselectAll>
3524
</div>
36-
)
37-
}
38-
}
39-
40-
export default List
25+
<div className="albums">
26+
{items.map(item => (
27+
<Card key={item.year} player={item.player} year={item.year} />
28+
))}
29+
</div>
30+
</div>
31+
)
32+
})

example/src/sample-data.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const players = [
2929
'Gary Payton',
3030
'Ray Allen',
3131
'Dwight Howard',
32-
'Chris Paul'
32+
'Chris Paul',
3333
]
3434

3535
export type TAlbumItem = {
@@ -39,5 +39,5 @@ export type TAlbumItem = {
3939

4040
export const items = Array.from({ length: 500 }).map((_, index) => ({
4141
year: index + 1,
42-
player: players[index % players.length]
42+
player: players[index % players.length],
4343
}))

package.json

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"transpile": "tsc",
1515
"build:prod": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js",
1616
"prepublishOnly": "yarn lint && yarn clean && yarn build:prod && yarn transpile",
17-
"prettier": "prettier --write src/**/*.{js,ts,tsx} example/src/**/*.{js,ts,tsx} .js",
17+
"prettier": "prettier --write src/**/*.{ts,tsx} example/src/**/*.{ts,tsx}",
1818
"lint:basic": "tsc --pretty --noEmit && eslint --fix --format codeframe",
1919
"lint": "yarn lint:basic '{example/,}src/**/*.{ts,tsx}'",
2020
"format": "yarn prettier && yarn lint",
@@ -70,37 +70,35 @@
7070
},
7171
"devDependencies": {
7272
"@babel/cli": "^7.8.4",
73-
"@babel/core": "^7.8.4",
73+
"@babel/core": "^7.9.0",
7474
"@babel/plugin-proposal-class-properties": "^7.8.3",
75-
"@babel/preset-env": "^7.8.4",
76-
"@babel/preset-react": "^7.8.3",
77-
"@babel/preset-typescript": "^7.8.3",
78-
"@types/react": "^16.9.22",
79-
"@types/react-dom": "^16.9.5",
80-
"@typescript-eslint/eslint-plugin": "^2.17.0",
81-
"@typescript-eslint/parser": "^2.17.0",
82-
"babel-loader": "^8.0.6",
83-
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
84-
"cross-env": "^7.0.0",
75+
"@babel/preset-env": "^7.9.5",
76+
"@babel/preset-react": "^7.9.4",
77+
"@babel/preset-typescript": "^7.9.0",
78+
"@types/react": "^16.9.6",
79+
"@types/react-dom": "^16.9.6",
80+
"@typescript-eslint/eslint-plugin": "^2.27.0",
81+
"@typescript-eslint/parser": "^2.27.0",
82+
"babel-loader": "^8.1.0",
83+
"cross-env": "^7.0.2",
8584
"eslint": "6.8.0",
86-
"eslint-config-airbnb": "18.0.1",
87-
"eslint-config-prettier": "^6.5.0",
88-
"eslint-plugin-import": "^2.18.2",
85+
"eslint-config-airbnb": "18.1.0",
86+
"eslint-config-prettier": "^6.10.1",
87+
"eslint-plugin-import": "^2.20.2",
8988
"eslint-plugin-jsx-a11y": "^6.2.3",
9089
"eslint-plugin-prettier": "^3.1.1",
91-
"eslint-plugin-react": "^7.18.0",
92-
"eslint-plugin-react-hooks": "^2.2.0",
93-
"husky": "^4.2.3",
94-
"lint-staged": "^10.0.7",
95-
"prettier": "^1.18.2",
96-
"prop-types": "^15.7.2",
97-
"react": "^16.8.6",
98-
"react-dom": "^16.8.6",
99-
"release-it": "^12.6.1",
90+
"eslint-plugin-react": "^7.19.0",
91+
"eslint-plugin-react-hooks": "^3.0.0",
92+
"husky": "^4.2.5",
93+
"lint-staged": "^10.1.3",
94+
"prettier": "^2.0.4",
95+
"react": "^16.13.1",
96+
"react-dom": "^16.13.1",
97+
"release-it": "^13.5.2",
10098
"rimraf": "^3.0.2",
101-
"typescript": "^3.8.2",
102-
"webpack": "^4.41.6",
103-
"webpack-bundle-analyzer": "^3.3.2",
99+
"typescript": "^3.8.3",
100+
"webpack": "^4.42.1",
101+
"webpack-bundle-analyzer": "^3.6.1",
104102
"webpack-cli": "^3.3.11",
105103
"webpack-dev-server": "^3.10.3"
106104
}

0 commit comments

Comments
 (0)