docs: initial examples and outline for cache blog post

This commit is contained in:
Corbin Crutchley
2023-12-16 06:13:12 -08:00
parent 4fe0adb2dd
commit ac5ccbd7e7
19 changed files with 6907 additions and 0 deletions

View 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>;
}
```

View 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?

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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"
}
}

View File

@@ -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 />);

View File

@@ -0,0 +1,6 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
});

View 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?

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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"
}
}

View File

@@ -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 />);

View File

@@ -0,0 +1,6 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
});

View 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?

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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"
}
}

View File

@@ -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 />);

View File

@@ -0,0 +1,6 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
});