Skip to content

Commit 0af0258

Browse files
committed
Working hooks
0 parents  commit 0af0258

File tree

12 files changed

+428
-0
lines changed

12 files changed

+428
-0
lines changed

.babelrc.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
presets: [
3+
[
4+
'@babel/env',
5+
{
6+
loose: true,
7+
modules: false,
8+
},
9+
],
10+
'@babel/react',
11+
],
12+
plugins: [
13+
process.env.NODE_ENV === 'test' && '@babel/transform-modules-commonjs',
14+
].filter(Boolean),
15+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dist

.huskyrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"hooks": {
3+
"pre-commit": "lint-staged"
4+
}
5+
}

.lintstagedrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"*.{js,md}": [
3+
"prettier --single-quote --no-semi --trailing-comma=all --write",
4+
"git add"
5+
]
6+
}

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
language: node_js
2+
3+
node_js:
4+
- "node"

README.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# react-selector-hooks
2+
3+
Collection of hook-based memoized selector factories for declarations outside of render.
4+
5+
## Motivation
6+
7+
Reusing existing functions. It also might often be desirable to declare selector functions outside of render to keep render functions less bloated.
8+
9+
Instead of this:
10+
11+
```jsx
12+
function Component({ a, b }) {
13+
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
14+
return <span>{memoizedValue}</span>
15+
}
16+
```
17+
18+
You can write this:
19+
20+
```jsx
21+
const useSelector = createSelector(computeExpensiveValue)
22+
23+
function Component({ a, b }) {
24+
const memoizedValue = useSelector(a, b)
25+
return <span>{memoizedValue}</span>
26+
}
27+
```
28+
29+
## API
30+
31+
### createSelector(resultFunc)
32+
33+
```jsx
34+
import * as React from 'react'
35+
import { createSelector } from 'react-selector-hooks'
36+
37+
const useSelector = createSelector(computeExpensiveValue)
38+
39+
export default function Component({ a, b }) {
40+
const memoizedValue = useSelector(a, b)
41+
return <span>{memoizedValue}</span>
42+
}
43+
```
44+
45+
### createStateSelector([inputSelectors], resultFunc)
46+
47+
This is really similar to [`reselect's createSelector`](https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc).
48+
49+
```jsx
50+
import * as React from 'react'
51+
import { createStateSelector } from 'react-selector-hooks'
52+
53+
const useSelector = createStateSelector(
54+
[
55+
state => state.values.value1,
56+
(state, a) => state.values.value2 + a,
57+
(state, a) => state.values.value3 * a,
58+
],
59+
(value1, value2, value3) => value1 + value2,
60+
)
61+
62+
export default function Component({ a }) {
63+
const state = React.useContext(StoreContext)
64+
const memoizedValue = useSelector(state, a)
65+
return <span>{memoizedValue}</span>
66+
}
67+
```
68+
69+
### createStructuredSelector({...inputSelectors}, resultFunc)
70+
71+
This is really similar to [`reselect's createStructuredSelector`](https://github.com/reduxjs/reselect#createstructuredselectorinputselectors-selectorcreator--createselector).
72+
73+
```jsx
74+
import * as React from 'react'
75+
import { createStructuredSelector } from 'react-selector-hooks'
76+
77+
const useSelector = createStructuredSelector(
78+
{
79+
value1: state => state.values.value1,
80+
value2: (state, a) => state.values.value2 + a,
81+
},
82+
({ value1, value2 }) => value1 + value2,
83+
)
84+
85+
export default function Component({ a }) {
86+
const state = React.useContext(StoreContext)
87+
const memoizedValue = useSelector(state, a)
88+
return <span>{memoizedValue}</span>
89+
}
90+
```

__tests__/__snapshots__/index.js.snap

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`createSelector 1`] = `
4+
<span>
5+
5
6+
</span>
7+
`;
8+
9+
exports[`createSelector 2`] = `
10+
<span>
11+
5
12+
</span>
13+
`;
14+
15+
exports[`createSelector 3`] = `
16+
<span>
17+
7
18+
</span>
19+
`;
20+
21+
exports[`createStateSelector 1`] = `
22+
<span>
23+
5
24+
</span>
25+
`;
26+
27+
exports[`createStateSelector 2`] = `
28+
<span>
29+
5
30+
</span>
31+
`;
32+
33+
exports[`createStateSelector 3`] = `
34+
<span>
35+
13
36+
</span>
37+
`;
38+
39+
exports[`createStateSelector 4`] = `
40+
<span>
41+
13
42+
</span>
43+
`;
44+
45+
exports[`createStateSelector 5`] = `
46+
<span>
47+
16
48+
</span>
49+
`;
50+
51+
exports[`createStructuredSelector 1`] = `
52+
<span>
53+
5
54+
</span>
55+
`;
56+
57+
exports[`createStructuredSelector 2`] = `
58+
<span>
59+
5
60+
</span>
61+
`;
62+
63+
exports[`createStructuredSelector 3`] = `
64+
<span>
65+
13
66+
</span>
67+
`;
68+
69+
exports[`createStructuredSelector 4`] = `
70+
<span>
71+
13
72+
</span>
73+
`;
74+
75+
exports[`createStructuredSelector 5`] = `
76+
<span>
77+
16
78+
</span>
79+
`;

__tests__/index.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import * as React from 'react'
2+
import TestRenderer from 'react-test-renderer'
3+
import {
4+
createSelector,
5+
createStateSelector,
6+
createStructuredSelector,
7+
} from '../src'
8+
9+
const render = element => {
10+
const renderer = TestRenderer.create(element)
11+
return {
12+
rerender(element) {
13+
renderer.update(element)
14+
},
15+
snapshot() {
16+
expect(renderer.toJSON()).toMatchSnapshot()
17+
},
18+
}
19+
}
20+
21+
test('createSelector', () => {
22+
const selector = jest.fn((a, b) => a + b)
23+
const useSelector = createSelector(selector)
24+
25+
function Test({ a, b }) {
26+
const result = useSelector(a, b)
27+
return <span>{result}</span>
28+
}
29+
30+
const { rerender, snapshot } = render(<Test a={2} b={3} />)
31+
expect(selector).toBeCalledTimes(1)
32+
expect(selector).toHaveBeenCalledWith(2, 3)
33+
expect(selector).toHaveReturnedWith(5)
34+
snapshot()
35+
36+
rerender(<Test a={2} b={3} />)
37+
expect(selector).toBeCalledTimes(1)
38+
snapshot()
39+
40+
rerender(<Test a={4} b={3} />)
41+
expect(selector).toBeCalledTimes(2)
42+
expect(selector).toHaveReturnedWith(7)
43+
snapshot()
44+
})
45+
46+
test('createStateSelector', () => {
47+
const selector = jest.fn((a, b) => a + b)
48+
const useSelector = createStateSelector(
49+
[(state, path) => state[path], state => state.b],
50+
selector,
51+
)
52+
53+
function Test({ state, path }) {
54+
const result = useSelector(state, path)
55+
return <span>{result}</span>
56+
}
57+
58+
const obj = { a: 2, b: 3, c: 10 }
59+
const { rerender, snapshot } = render(<Test path="a" state={obj} />)
60+
expect(selector).toBeCalledTimes(1)
61+
expect(selector).toHaveBeenCalledWith(2, 3)
62+
expect(selector).toHaveReturnedWith(5)
63+
snapshot()
64+
65+
rerender(<Test path="a" state={obj} />)
66+
expect(selector).toBeCalledTimes(1)
67+
snapshot()
68+
69+
rerender(<Test path="c" state={obj} />)
70+
expect(selector).toBeCalledTimes(2)
71+
expect(selector).toHaveBeenCalledWith(10, 3)
72+
expect(selector).toHaveReturnedWith(13)
73+
snapshot()
74+
75+
const obj2 = { ...obj }
76+
rerender(<Test path="c" state={obj2} />)
77+
expect(selector).toBeCalledTimes(2)
78+
snapshot()
79+
80+
const obj3 = { ...obj2, b: 6 }
81+
rerender(<Test path="c" state={obj3} />)
82+
expect(selector).toBeCalledTimes(3)
83+
expect(selector).toHaveBeenCalledWith(10, 6)
84+
expect(selector).toHaveReturnedWith(16)
85+
snapshot()
86+
})
87+
88+
test('createStructuredSelector', () => {
89+
const selector = jest.fn(({ x, y }) => x + y)
90+
const useSelector = createStructuredSelector(
91+
{
92+
x: (state, path) => state[path],
93+
y: state => state.b,
94+
},
95+
selector,
96+
)
97+
98+
function Test({ state, path }) {
99+
const result = useSelector(state, path)
100+
return <span>{result}</span>
101+
}
102+
103+
const obj = { a: 2, b: 3, c: 10 }
104+
const { rerender, snapshot } = render(<Test path="a" state={obj} />)
105+
expect(selector).toBeCalledTimes(1)
106+
expect(selector.mock.calls[0][0]).toEqual({ x: 2, y: 3 })
107+
expect(selector).toHaveReturnedWith(5)
108+
snapshot()
109+
110+
rerender(<Test path="a" state={obj} />)
111+
expect(selector).toBeCalledTimes(1)
112+
snapshot()
113+
114+
rerender(<Test path="c" state={obj} />)
115+
expect(selector).toBeCalledTimes(2)
116+
expect(selector.mock.calls[1][0]).toEqual({ x: 10, y: 3 })
117+
expect(selector).toHaveReturnedWith(13)
118+
snapshot()
119+
120+
const obj2 = { ...obj }
121+
rerender(<Test path="c" state={obj2} />)
122+
expect(selector).toBeCalledTimes(2)
123+
snapshot()
124+
125+
const obj3 = { ...obj2, b: 6 }
126+
rerender(<Test path="c" state={obj3} />)
127+
expect(selector).toBeCalledTimes(3)
128+
expect(selector.mock.calls[2][0]).toEqual({ x: 10, y: 6 })
129+
expect(selector).toHaveReturnedWith(16)
130+
snapshot()
131+
})

package.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "react-selector-hooks",
3+
"version": "0.0.0",
4+
"description": "Collection of hook-based selector factories for declarations outside of render.",
5+
"main": "./dist/react-selector-hooks.js",
6+
"module": "./dist/react-selector-hooks.esm.js",
7+
"files": [
8+
"dist"
9+
],
10+
"scripts": {
11+
"test": "jest --env=node",
12+
"prebuild": "rimraf dist",
13+
"build": "rollup -c",
14+
"preversion": "npm test",
15+
"prepare": "npm run build"
16+
},
17+
"repository": {
18+
"type": "git",
19+
"url": "git+https://github.com/Andarist/react-selector-hooks.git"
20+
},
21+
"author": "",
22+
"license": "MIT",
23+
"bugs": {
24+
"url": "https://github.com/Andarist/react-selector-hooks/issues"
25+
},
26+
"homepage": "https://github.com/Andarist/react-selector-hooks#readme",
27+
"peerDependencies": {
28+
"react": "^16.7.0-alpha.0"
29+
},
30+
"devDependencies": {
31+
"@babel/core": "^7.1.2",
32+
"@babel/plugin-transform-modules-commonjs": "^7.1.0",
33+
"@babel/preset-env": "^7.1.0",
34+
"@babel/preset-react": "^7.0.0",
35+
"husky": "^1.1.0",
36+
"jest": "^23.6.0",
37+
"lint-staged": "^7.3.0",
38+
"prettier": "^1.14.3",
39+
"react": "16.7.0-alpha.0",
40+
"react-test-renderer": "^16.7.0-alpha.0",
41+
"rimraf": "^2.6.2",
42+
"rollup": "^0.66.6",
43+
"rollup-plugin-babel": "^4.0.3"
44+
}
45+
}

0 commit comments

Comments
 (0)