Skip to content

Commit 915b0f1

Browse files
feat: use dataloader (#134)
* feat: use dataloader * feat: drop use paginated dataloader * feat: improve dataloader
1 parent 49113d4 commit 915b0f1

File tree

11 files changed

+1117
-1
lines changed

11 files changed

+1117
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"jest-localstorage-mock"
6767
],
6868
"collectCoverageFrom": [
69-
"packages/*/src/*.{js,jsx}"
69+
"packages/*/src/**/*.{js,jsx}"
7070
],
7171
"modulePathIgnorePatterns": [
7272
"locales"

packages/use-dataloader/.npmignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
**/__tests__/**
2+
src
3+
!.npmignore

packages/use-dataloader/README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# `@scaleway/use-dataloader`
2+
3+
## Tiny hooks to help you manage asynchronous tasks
4+
5+
- Lightweight
6+
- Easy to use
7+
- Easy to integrate with Axios, fetch, or any Promise-based tool
8+
9+
## Install
10+
11+
```bash
12+
$ yarn add @scaleway/use-dataloader
13+
```
14+
15+
## How to use
16+
17+
### Add DataLoaderProvider to your app
18+
19+
Firstly you need to put the context at the top of your App.
20+
21+
```js
22+
import { DataLoaderProvider } from '@scaleway-lib/use-dataloader'
23+
import React from 'react'
24+
import ReactDOM from 'react-dom'
25+
import App from './App'
26+
27+
ReactDOM.render(
28+
<React.StrictMode>
29+
<DataLoaderProvider>
30+
<App />
31+
</DataLoaderProvider>
32+
</React.StrictMode>,
33+
document.getElementById('root'),
34+
)
35+
```
36+
37+
Now you can use `useDataLoader` and `useDataLoaderContext` in your App
38+
39+
### useDataLoader
40+
41+
```js
42+
import { useDataLoader } from '@scaleway-lib/use-dataloader'
43+
44+
const fakePromise = () =>
45+
new Promise(resolve => setTimeout(resolve('test'), 1000))
46+
47+
function MyComponent() {
48+
// Use a key if you want to persist data in the DataLoaderProvider cache
49+
const { data, isLoading, isSuccess, isError, error } = useDataLoader(
50+
'cache-key',
51+
fakePromise,
52+
)
53+
54+
// Will be true during the promise
55+
if (isLoading) {
56+
return <div>Loading...</div>
57+
}
58+
59+
// Will be true when the promise is resolved
60+
if (isSuccess) {
61+
// Will display "test" in the the div
62+
return <div>{data}</div>
63+
}
64+
65+
// Will be false when the promise is rejected
66+
if (isError) {
67+
// Will display the error in the the div
68+
return <div>{error}</div>
69+
}
70+
}
71+
72+
export default MyComponent
73+
```
74+
75+
### useDataLoaderContext
76+
77+
```js
78+
import { useDataLoaderContext } from '@scaleway-lib/use-dataloader'
79+
80+
const fakePromise = () =>
81+
new Promise(resolve => {
82+
setTimeout(resolve('test'), 1000):
83+
})
84+
85+
86+
function MyComponentThatUseDataLoader({key}) {
87+
// Use a key if you want to persist data in the DataLoaderProvider cache
88+
const { data, isLoading, isSuccess, isError, error } = useDataLoader(
89+
key,
90+
fakePromise,
91+
)
92+
93+
// Will be true during the promise
94+
if (isLoading) {
95+
return <div>Loading...</div>
96+
}
97+
98+
// Will be true when the promise is resolved
99+
if (isSuccess) {
100+
// Will display "test" in the the div
101+
return <div>{data}</div>
102+
}
103+
104+
// Will be false when the promise is rejected
105+
if (isError) {
106+
// Will display the error in the the div
107+
return <div>{error}</div>
108+
}
109+
}
110+
111+
112+
function MyComponent() {
113+
const { reloadAll, reload } = useDataLoaderContext();
114+
115+
const handleReload = (keyToReload) => () => {
116+
await reload(keyToReload) // Execute the method
117+
}
118+
}
119+
120+
const handleReloadAll = () => {
121+
await reloadAll()
122+
}
123+
}
124+
125+
return (
126+
<div>
127+
<MyComponentThatUseDataLoader key="test" />
128+
<MyComponentThatUseDataLoader key="test-2" />
129+
<button onClick={handleReload("test")}>Reload first</button>
130+
<button onClick={handleReload("test-2")}>Reload second</button>
131+
<button onClick={handleReloadAll}>Reload all</button>
132+
</div>
133+
)
134+
}
135+
136+
export default MyComponent
137+
```
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@scaleway/use-dataloader",
3+
"version": "1.0.0",
4+
"description": "A small hook to handle api requests",
5+
"keywords": [
6+
"react",
7+
"reactjs",
8+
"hooks",
9+
"dataloader"
10+
],
11+
"main": "dist/index.js",
12+
"module": "dist/module.js",
13+
"browser": {
14+
"dist/index.js": "./dist/index.browser.js",
15+
"dist/module.js": "./dist/module.browser.js"
16+
},
17+
"publishConfig": {
18+
"access": "public"
19+
},
20+
"repository": {
21+
"type": "git",
22+
"url": "https://github.com/scaleway/scaleway-lib",
23+
"directory": "packages/use-dataloader"
24+
},
25+
"license": "MIT",
26+
"dependencies": {
27+
"prop-types": "^15.7.2"
28+
},
29+
"peerDependencies": {
30+
"react": "17.x"
31+
},
32+
"devDependencies": {
33+
"@testing-library/jest-dom": "^5.11.9",
34+
"@testing-library/react": "^11.2.5",
35+
"@testing-library/react-hooks": "^5.1.1",
36+
"react": "^17.0.1"
37+
}
38+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import PropTypes from 'prop-types'
2+
import React, {
3+
createContext,
4+
useCallback,
5+
useContext,
6+
useMemo,
7+
useRef,
8+
} from 'react'
9+
10+
export const DataLoaderContext = createContext()
11+
12+
const DataLoaderProvider = ({ children }) => {
13+
const cachedData = useRef({})
14+
const reloads = useRef({})
15+
16+
const setCachedData = useCallback(compute => {
17+
if (typeof compute === 'function') {
18+
cachedData.current = compute(cachedData.current)
19+
} else {
20+
cachedData.current = compute
21+
}
22+
}, [])
23+
24+
const setReloads = useCallback(compute => {
25+
if (typeof compute === 'function') {
26+
reloads.current = compute(reloads.current)
27+
} else {
28+
reloads.current = compute
29+
}
30+
}, [])
31+
32+
const addCachedData = useCallback(
33+
(key, newData) => {
34+
if (key && typeof key === 'string' && newData) {
35+
setCachedData(actualCachedData => ({
36+
...actualCachedData,
37+
[key]: newData,
38+
}))
39+
}
40+
},
41+
[setCachedData],
42+
)
43+
44+
const addReload = useCallback(
45+
(key, method) => {
46+
if (key && typeof key === 'string' && method) {
47+
setReloads(actualReloads => ({
48+
...actualReloads,
49+
[key]: method,
50+
}))
51+
}
52+
},
53+
[setReloads],
54+
)
55+
56+
const clearReload = useCallback(
57+
key => {
58+
if (key && typeof key === 'string') {
59+
setReloads(actualReloads => {
60+
const tmp = actualReloads
61+
delete tmp[key]
62+
63+
return tmp
64+
})
65+
}
66+
},
67+
[setReloads],
68+
)
69+
70+
const clearAllReloads = useCallback(() => {
71+
setReloads({})
72+
}, [setReloads])
73+
74+
const clearCachedData = useCallback(
75+
key => {
76+
if (key && typeof key === 'string') {
77+
setCachedData(actualCachedData => {
78+
const tmp = actualCachedData
79+
delete tmp[key]
80+
81+
return tmp
82+
})
83+
}
84+
},
85+
[setCachedData],
86+
)
87+
const clearAllCachedData = useCallback(() => {
88+
setCachedData({})
89+
}, [setCachedData])
90+
91+
const reload = useCallback(async key => {
92+
if (key && typeof key === 'string') {
93+
await (reloads.current[key] && reloads.current[key]())
94+
}
95+
}, [])
96+
97+
const reloadAll = useCallback(async () => {
98+
await Promise.all(
99+
Object.values(reloads.current).map(reloadFn => reloadFn()),
100+
)
101+
}, [])
102+
103+
const getCachedData = useCallback(key => {
104+
if (key) {
105+
return cachedData.current[key] || undefined
106+
}
107+
108+
return cachedData.current
109+
}, [])
110+
111+
const getReloads = useCallback(key => {
112+
if (key) {
113+
return reloads.current[key] || undefined
114+
}
115+
116+
return reloads.current
117+
}, [])
118+
119+
const value = useMemo(
120+
() => ({
121+
addCachedData,
122+
clearCachedData,
123+
getCachedData,
124+
reload,
125+
getReloads,
126+
addReload,
127+
clearReload,
128+
clearAllCachedData,
129+
clearAllReloads,
130+
reloadAll,
131+
}),
132+
[
133+
addCachedData,
134+
clearReload,
135+
clearCachedData,
136+
getCachedData,
137+
getReloads,
138+
addReload,
139+
reload,
140+
clearAllCachedData,
141+
clearAllReloads,
142+
reloadAll,
143+
],
144+
)
145+
146+
return (
147+
<DataLoaderContext.Provider value={value}>
148+
{children}
149+
</DataLoaderContext.Provider>
150+
)
151+
}
152+
153+
DataLoaderProvider.propTypes = {
154+
children: PropTypes.node.isRequired,
155+
}
156+
157+
export const useDataLoaderContext = () => useContext(DataLoaderContext)
158+
159+
export default DataLoaderProvider

0 commit comments

Comments
 (0)