examples: Update redis example with useOptimistic
(#60596)
This commit is contained in:
parent
ef1b66a518
commit
b177ff6488
3 changed files with 61 additions and 47 deletions
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import clsx from "clsx";
|
||||
import { useOptimistic, useRef } from "react";
|
||||
import { useOptimistic, useRef, useTransition } from "react";
|
||||
import { saveFeature, upvote } from "./actions";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Feature } from "./types";
|
||||
|
@ -23,22 +23,26 @@ function Item({
|
|||
pending: boolean;
|
||||
mutate: any;
|
||||
}) {
|
||||
const upvoteWithId = upvote.bind(null, feature);
|
||||
let upvoteWithId = upvote.bind(null, feature);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let [isPending, startTransition] = useTransition();
|
||||
|
||||
return (
|
||||
<form
|
||||
action={upvoteWithId}
|
||||
onSubmit={async (event) => {
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
mutate({
|
||||
updatedFeature: {
|
||||
...feature,
|
||||
score: Number(feature.score) + 1,
|
||||
},
|
||||
pending: true,
|
||||
});
|
||||
|
||||
await upvote(feature);
|
||||
startTransition(async () => {
|
||||
mutate({
|
||||
updatedFeature: {
|
||||
...feature,
|
||||
score: Number(feature.score) + 1,
|
||||
},
|
||||
pending: true,
|
||||
});
|
||||
await upvote(feature);
|
||||
});
|
||||
}}
|
||||
className={clsx(
|
||||
"p-6 mx-8 flex items-center border-t border-l border-r",
|
||||
|
@ -73,8 +77,8 @@ type FeatureState = {
|
|||
};
|
||||
|
||||
export default function FeatureForm({ features }: { features: Feature[] }) {
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
const [state, mutate] = useOptimistic(
|
||||
let formRef = useRef<HTMLFormElement>(null);
|
||||
let [state, mutate] = useOptimistic(
|
||||
{ features, pending: false },
|
||||
function createReducer(state, newState: FeatureState) {
|
||||
if (newState.newFeature) {
|
||||
|
@ -112,6 +116,8 @@ export default function FeatureForm({ features }: { features: Feature[] }) {
|
|||
score: "1",
|
||||
};
|
||||
let saveWithNewFeature = saveFeature.bind(null, featureStub);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let [isPending, startTransition] = useTransition();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -120,7 +126,7 @@ export default function FeatureForm({ features }: { features: Feature[] }) {
|
|||
className="relative my-8"
|
||||
ref={formRef}
|
||||
action={saveWithNewFeature}
|
||||
onSubmit={async (event) => {
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
let formData = new FormData(event.currentTarget);
|
||||
let newFeature = {
|
||||
|
@ -128,12 +134,15 @@ export default function FeatureForm({ features }: { features: Feature[] }) {
|
|||
title: formData.get("feature") as string,
|
||||
};
|
||||
|
||||
mutate({
|
||||
newFeature,
|
||||
pending: true,
|
||||
});
|
||||
formRef.current?.reset();
|
||||
await saveFeature(newFeature, formData);
|
||||
startTransition(async () => {
|
||||
mutate({
|
||||
newFeature,
|
||||
pending: true,
|
||||
});
|
||||
|
||||
await saveFeature(newFeature, formData);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<input
|
||||
|
|
|
@ -30,27 +30,32 @@ function VercelLogo(props: React.SVGProps<SVGSVGElement>) {
|
|||
}
|
||||
|
||||
async function getFeatures() {
|
||||
let itemIds = await kv.zrange("items_by_score", 0, 100, {
|
||||
rev: true,
|
||||
});
|
||||
try {
|
||||
let itemIds = await kv.zrange("items_by_score", 0, 100, {
|
||||
rev: true,
|
||||
});
|
||||
|
||||
if (!itemIds.length) {
|
||||
if (!itemIds.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let multi = kv.multi();
|
||||
itemIds.forEach((id) => {
|
||||
multi.hgetall(`item:${id}`);
|
||||
});
|
||||
|
||||
let items: Feature[] = await multi.exec();
|
||||
return items.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
score: item.score,
|
||||
created_at: item.created_at,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
|
||||
let multi = kv.multi();
|
||||
itemIds.forEach((id) => {
|
||||
multi.hgetall(`item:${id}`);
|
||||
});
|
||||
|
||||
let items: Feature[] = await multi.exec();
|
||||
return items.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
score: item.score,
|
||||
created_at: item.created_at,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default async function Page() {
|
||||
|
|
|
@ -6,20 +6,20 @@
|
|||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "20.10.3",
|
||||
"@types/react": "18.2.42",
|
||||
"@types/react-dom": "18.2.17",
|
||||
"@types/node": "20.11.0",
|
||||
"@types/react": "18.2.47",
|
||||
"@types/react-dom": "18.2.18",
|
||||
"@types/uuid": "9.0.7",
|
||||
"@vercel/kv": "^1.0.0",
|
||||
"@vercel/kv": "^1.0.1",
|
||||
"autoprefixer": "10.4.16",
|
||||
"clsx": "^2.0.0",
|
||||
"geist": "^1.2.0",
|
||||
"next": "canary",
|
||||
"postcss": "^8.4.32",
|
||||
"clsx": "^2.1.0",
|
||||
"geist": "^1.2.1",
|
||||
"next": "latest",
|
||||
"postcss": "^8.4.33",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"tailwindcss": "3.3.6",
|
||||
"typescript": "5.3.2",
|
||||
"tailwindcss": "3.4.1",
|
||||
"typescript": "5.3.3",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue