mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 21:07:49 +00:00
docs: initial examples and outline for cache blog post
This commit is contained in:
89
content/blog/explaining-reacts-cache-function/index.md
Normal file
89
content/blog/explaining-reacts-cache-function/index.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
title: "Explaining React's cache Function",
|
||||||
|
description: "",
|
||||||
|
published: '2023-12-17T21:52:59.284Z',
|
||||||
|
authors: ['crutchcorn'],
|
||||||
|
tags: ['react', 'webdev', 'javascript'],
|
||||||
|
attached: [],
|
||||||
|
license: 'cc-by-4'
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
> Some warning about how this is an experimental API and may change in the future
|
||||||
|
|
||||||
|
1) Compare/contrast React useMemo/useCallback/memo/cache
|
||||||
|
2) Show how `cache` persists between functions
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { cache, useReducer, useState } from "react";
|
||||||
|
|
||||||
|
const test = cache((id) => {
|
||||||
|
alert(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [counter, setCounter] = useState(0);
|
||||||
|
const [_, rerender] = useReducer(() => ({}), {});
|
||||||
|
test(counter);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
|
||||||
|
<button onClick={rerender}>Rerender</button>
|
||||||
|
<input key={Math.floor(Math.random() * 10)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")).render(<App />);
|
||||||
|
```
|
||||||
|
|
||||||
|
Mention this as useful when combined with React's `use` Hook
|
||||||
|
|
||||||
|
It even caches results:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
const getIsEvenOrOdd = cache(
|
||||||
|
(number) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(number % 2 === 0 ? "Even" : "Odd");
|
||||||
|
}, 2000);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [counter, setCounter] = useState(0);
|
||||||
|
|
||||||
|
const isEvenOrOdd = getIsEvenOrOdd(counter);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
|
||||||
|
<p>
|
||||||
|
{counter} is {isEvenOrOdd}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And errors:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
const getIsEven = cache((number) => {
|
||||||
|
alert("I am checking if " + number + " is even or not");
|
||||||
|
if (number % 2 === 0) {
|
||||||
|
throw "Number is even";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Even if you render this component multiple times, it will only perform the
|
||||||
|
// calculation needed to throw the error once.
|
||||||
|
function ThrowAnErrorIfEven({ number, instance }) {
|
||||||
|
getIsEven(number);
|
||||||
|
|
||||||
|
return <p>I am instance #{instance}</p>;
|
||||||
|
}
|
||||||
|
```
|
||||||
24
content/blog/explaining-reacts-cache-function/react-basic-cache-usage/.gitignore
vendored
Normal file
24
content/blog/explaining-reacts-cache-function/react-basic-cache-usage/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>React Reactivity</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2056
content/blog/explaining-reacts-cache-function/react-basic-cache-usage/package-lock.json
generated
Normal file
2056
content/blog/explaining-reacts-cache-function/react-basic-cache-usage/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@unicorn-utterances/react-reactivity",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "18.3.0-canary-0cdfef19b-20231211",
|
||||||
|
"react-dom": "18.3.0-canary-0cdfef19b-20231211"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"vite": "^5.0.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { cache, useReducer, useState } from "react";
|
||||||
|
|
||||||
|
const test = cache((id) => {
|
||||||
|
alert(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [counter, setCounter] = useState(0);
|
||||||
|
const [_, rerender] = useReducer(() => ({}), {});
|
||||||
|
test(counter);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
|
||||||
|
<button onClick={rerender}>Rerender</button>
|
||||||
|
<input key={Math.floor(Math.random() * 10)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")).render(<App />);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
||||||
24
content/blog/explaining-reacts-cache-function/react-cache-error/.gitignore
vendored
Normal file
24
content/blog/explaining-reacts-cache-function/react-cache-error/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>React Reactivity</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2426
content/blog/explaining-reacts-cache-function/react-cache-error/package-lock.json
generated
Normal file
2426
content/blog/explaining-reacts-cache-function/react-cache-error/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@unicorn-utterances/react-reactivity",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "18.3.0-canary-0cdfef19b-20231211",
|
||||||
|
"react-dom": "18.3.0-canary-0cdfef19b-20231211"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"vite": "^5.0.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { cache, Component, useState } from "react";
|
||||||
|
|
||||||
|
const getIsEven = cache((number) => {
|
||||||
|
alert("I am checking if " + number + " is even or not");
|
||||||
|
if (number % 2 === 0) {
|
||||||
|
throw "Number is even";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function ThrowAnErrorIfEven({ number, instance }) {
|
||||||
|
getIsEven(number);
|
||||||
|
|
||||||
|
return <p>I am instance #{instance}</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [counter, setCounter] = useState(0);
|
||||||
|
const [howManyInstances, setHowManyInstances] = useState(3);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>You should only see one alert when you push the button below</p>
|
||||||
|
<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
|
||||||
|
{Array.from({ length: howManyInstances }).map((_, i) => (
|
||||||
|
<div key={i}>
|
||||||
|
<ErrorBoundary key={counter}>
|
||||||
|
<ThrowAnErrorIfEven key={i} number={counter} instance={i} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<p>Even if you add a thousand instances of the component</p>
|
||||||
|
<button onClick={() => setHowManyInstances((v) => v + 1)}>
|
||||||
|
Add instance of {"<ThrowAnErrorIfEven>"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ErrorBoundary extends Component {
|
||||||
|
state = { error: null };
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error) {
|
||||||
|
// Update state so the next render will show the fallback UI.
|
||||||
|
return { error };
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.error) {
|
||||||
|
return <p>There was an error: {this.state.error}</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")).render(<App />);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
||||||
24
content/blog/explaining-reacts-cache-function/react-cache-promise/.gitignore
vendored
Normal file
24
content/blog/explaining-reacts-cache-function/react-cache-promise/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>React Reactivity</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2056
content/blog/explaining-reacts-cache-function/react-cache-promise/package-lock.json
generated
Normal file
2056
content/blog/explaining-reacts-cache-function/react-cache-promise/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@unicorn-utterances/react-reactivity",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "18.3.0-canary-0cdfef19b-20231211",
|
||||||
|
"react-dom": "18.3.0-canary-0cdfef19b-20231211"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"vite": "^5.0.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { cache, useReducer, useState } from "react";
|
||||||
|
|
||||||
|
const getIsEvenOrOdd = cache(
|
||||||
|
(number) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(number % 2 === 0 ? "Even" : "Odd");
|
||||||
|
}, 2000);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [counter, setCounter] = useState(0);
|
||||||
|
|
||||||
|
const isEvenOrOdd = getIsEvenOrOdd(counter);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
|
||||||
|
<p>
|
||||||
|
{counter} is {isEvenOrOdd}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")).render(<App />);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user