Skip to content

Instantly share code, notes, and snippets.

@peerreynders
Last active September 21, 2020 23:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peerreynders/87bc7702a9da7c5c5392dc8d05f7df9a to your computer and use it in GitHub Desktop.
Save peerreynders/87bc7702a9da7c5c5392dc8d05f7df9a to your computer and use it in GitHub Desktop.
useRefreshCallback - custom hook that also avoids creating garbage functions
<!doctype html>
<html lang="eng">
<head>
<meta charset="utf-8"/>
<title>useCallback alternative BEFORE</title>
</head>
<body>
<script type="module">
import {
html, render, useState, useCallback
} from '//unpkg.com/htm/preact/standalone.mjs';
// Component Parts
const initialDelta = 1;
const incDelta = delta => delta + 1;
const initialCount = 0;
// Component
// contrived use of useCallback - "cost of checks" yada, yada
function App() {
const [delta, setDelta] = useState(initialDelta);
const [count, setCount] = useState(initialCount);
// useCallback avoids the changing props value
// issue of newly created functions that don't behave
// differently - but is doesn't address creating garbage
// (i.e. unnecessary) functions on the majority of calls
//
const stepDelta = useCallback(() => setDelta(incDelta), []);
const stepCount = useCallback(() => setCount(count => count + delta), [delta]);
return html`
<p>
<button onClick=${stepDelta}>Step Delta</button>
<button onClick=${stepCount}>Step</button>
</p>
<output>Delta: ${delta} Count: ${count}</output>
`;
}
render(html`<${App} />`, document.body);
</script>
</body>
</html>
<!doctype html>
<html lang="eng">
<head>
<meta charset="utf-8"/>
<title>useCallback alternative AFTER</title>
</head>
<body>
<script type="module">
import {
html, render, useState
} from '//unpkg.com/htm/preact/standalone.mjs';
// custom hook
function useRefreshCallback(refreshCallback, isEqual, deps) {
const [state, setCallback] = useState(null);
if (state !== null && isEqual(state.lastDeps, deps)) {
return state.callback;
}
const fresh = refreshCallback(deps);
setCallback({callback: fresh, lastDeps: deps});
return fresh;
}
// Component Parts
const initialDelta = 1;
const incDelta = delta => delta + 1;
const refreshDeltaCallback = ([setDelta]) => {
console.log('refresh delta callback');
return () => setDelta(incDelta);
};
const initialCount = 0;
const refreshCountCallback = ([delta, setCount]) => {
console.log('refresh count callback with delta:', delta);
const incCount = count => count + delta;
return () => setCount(incCount);
};
// Shallow comparison is good enough in **this** case
function depsEqual(oldDeps, deps) {
if (oldDeps.length !== deps.length) {
return false;
}
for (let i = 0; i < deps.length; ++i) {
if (oldDeps[i] !== deps[i]) {
return false;
}
}
return true;
}
// Component
// contrived use of useRefreshCallback - "cost of checks" yada, yada
function App() {
const [delta, setDelta] = useState(initialDelta);
const [count, setCount] = useState(initialCount);
// call `refreshCallback` function only if dependencies change
const stepDelta = useRefreshCallback(refreshDeltaCallback, depsEqual, [setDelta]);
const stepCount = useRefreshCallback(refreshCountCallback, depsEqual, [delta, setCount]);
return html`
<p>
<button onClick=${stepDelta}>Step Delta</button>
<button onClick=${stepCount}>Step</button>
</p>
<output>Delta: ${delta} Count: ${count}</output>
`;
}
render(html`<${App} />`, document.body);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment