Skip to content

Commit aeb2ddc

Browse files
John Richard Chipps-Hardingmarcelkornblum
John Richard Chipps-Harding
andauthored
Add changed property (#14)
* Add changed property * Added linting * Docs change * Added test to replicate one of the issues * Sorted failing tests * Sorted final warning * Misc * 100% test code coverage * Changed to callback * CHANGELOG * Make change optional. * Added further documentation on the risks involved with using change. * Misc * Update README.md Co-Authored-By: Marcel Kornblum <[email protected]>
1 parent 718a91a commit aeb2ddc

File tree

10 files changed

+1552
-26
lines changed

10 files changed

+1552
-26
lines changed

.eslintignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
coverage
3+
dist

.eslintrc

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"node": true,
5+
"es6": true,
6+
"jest": true
7+
},
8+
"extends": ["eslint:recommended", "plugin:react/recommended"],
9+
"plugins": ["react", "react-hooks"],
10+
"parser": "babel-eslint",
11+
"rules": {
12+
"react-hooks/rules-of-hooks": "error",
13+
"react-hooks/exhaustive-deps": "warn"
14+
},
15+
"settings": {
16+
"react": {
17+
"version": "detect"
18+
}
19+
}
20+
}

.github/workflows/nodejs.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ jobs:
2222
- name: Install latest npm
2323
run: npm install --global npm@latest
2424

25-
- name: npm install and test
25+
- name: npm install, lint and test
2626
run: |
2727
npm install
28+
npm run lint
2829
npm test -- --ci
2930
env:
3031
CI: true

.prettierignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
coverage
3+
dist
4+
public
5+
tmp

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to this project are documented in this file.
44

5+
## Unreleased
6+
7+
- Added: `changed` callback argument.
8+
59
## 0.1.3
610

711
- Added: `payload` and `method` arguments.

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ If you need a simple way to load data quickly this is for you. It allows you to
1313
You can optionally specify a polling interval and manually trigger a refresh. It also gracefully cancels any open requests if you decide to change the URL and restarts timers if the polling interval changes.
1414

1515
```javascript
16-
const { data, loading, error, refresh } = useApi("https://some-api.com", 10000);
16+
const { data, loading, changed, error, refresh } = useApi(
17+
"https://some-api.com",
18+
10000
19+
);
1720
```
1821

1922
## Installation
@@ -80,6 +83,7 @@ const PeopleSearch = () = {
8083
- `pollInterval` - How often to re-request updated data. Pass 0 to disable polling (the default behaviour).
8184
- `payload` - A data object to send in the request. If we are performing a GET request, it is appended into the querystring (e.g. `?keywords=hello`). If it is a POST request it is sent in the request body as JSON.
8285
- `method` - Set the request type, either `get` or `post`. (defaults to `get`)
86+
- `changed`: A function that is called if the data actually changed during the request. If this is specified, use-api does extra checking and compares old and new data. If data does not change, new data is not propagated and a redraw is saved. Please note, this may have performance repercussions if the data is large as it performs a deep comparison between new and old data to determine if they are equivalent.
8387

8488
### Output
8589

index.js

+36-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
const { useEffect, useState } = require("react");
1+
const { useEffect, useState, useRef } = require("react");
22
const axios = require("axios");
3+
const isEqual = require("lodash.isequal");
34

45
const { CancelToken } = axios;
56

6-
const useApi = (apiEndpoint, pollInterval, payload, method = "get") => {
7+
const useApi = (
8+
apiEndpoint,
9+
pollInterval,
10+
payload,
11+
method = "get",
12+
changed
13+
) => {
714
const [data, setData] = useState({});
815
const [error, setError] = useState(null);
916
const [loading, setLoading] = useState(true);
1017
const [poll, setPoll] = useState(0);
18+
const lastData = useRef(data);
1119

12-
// Function to force a refresh
13-
const refresh = () => setPoll(poll + 1);
20+
if (method.toLowerCase) method = method.toLowerCase();
1421

1522
useEffect(() => {
1623
let timeout;
1724

18-
if (method.toLowerCase) method = method.toLowerCase();
19-
2025
if (!["get", "post"].includes(method)) {
2126
setLoading(false);
2227
setError("Invalid request method type, must be either post or get.");
@@ -39,8 +44,18 @@ const useApi = (apiEndpoint, pollInterval, payload, method = "get") => {
3944
.then(response => {
4045
// Make sure there are no errors reported
4146
setError(null);
42-
// Set the recieved data
43-
setData(response.data);
47+
48+
// Only do change detection if change is defined.
49+
if (changed) {
50+
if (!isEqual(response.data, lastData.current)) {
51+
// Set the received data ONLY IF its changed, redraw performance gain!
52+
lastData.current = response.data;
53+
setData(response.data);
54+
changed(response.data);
55+
}
56+
} else {
57+
setData(response.data);
58+
}
4459
})
4560
.catch(thrown => {
4661
// Only error on genuine errors, not cancellations
@@ -51,17 +66,27 @@ const useApi = (apiEndpoint, pollInterval, payload, method = "get") => {
5166
setLoading(false);
5267

5368
// Poll if specified to do so
54-
if (pollInterval) timeout = setTimeout(refresh, pollInterval);
69+
if (pollInterval)
70+
timeout = setTimeout(() => setPoll(poll + 1), pollInterval);
5571
});
5672

5773
// Cleanup, clear a timeout and cancel the request.
5874
return () => {
5975
if (timeout) clearTimeout(timeout);
6076
source.cancel();
6177
};
62-
}, [poll, apiEndpoint, pollInterval]);
78+
}, [
79+
poll,
80+
setPoll,
81+
apiEndpoint,
82+
pollInterval,
83+
payload,
84+
method,
85+
lastData,
86+
changed
87+
]);
6388

64-
return { data, loading, error, refresh };
89+
return { data, loading, changed, error, refresh: () => setPoll(poll + 1) };
6590
};
6691

6792
module.exports = useApi;

0 commit comments

Comments
 (0)