Skip to content

Commit f8bc87f

Browse files
authored
fix(changelog): Use useActionState for server actions (#11436)
1 parent 40bfe16 commit f8bc87f

File tree

7 files changed

+253
-189
lines changed

7 files changed

+253
-189
lines changed
Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
import {Fragment, Suspense} from 'react';
1+
import {Fragment, Suspense, useActionState} from 'react';
22
import Link from 'next/link';
33

44
import {prismaClient} from '@/server/prisma-client';
5-
import {editChangelog} from '@/server/actions/changelog';
6-
import {TitleSlug} from '@/client/components/titleSlug';
7-
import {FileUpload} from '@/client/components/fileUpload';
8-
import {Select} from '@/client/components/ui/Select';
9-
import {ForwardRefEditor} from '@/client/components/forwardRefEditor';
10-
import {Button} from '@/client/components/ui/Button';
5+
import {EditChangelogForm} from '@/client/components/forms/editChangelogForm';
116

127
export default async function ChangelogCreatePage({params}: {params: {id: string}}) {
138
const categories = await prismaClient.category.findMany({
@@ -38,59 +33,7 @@ export default async function ChangelogCreatePage({params}: {params: {id: string
3833

3934
return (
4035
<section className="overflow-x-auto col-start-3 col-span-8">
41-
<form action={editChangelog} className="px-2 w-full">
42-
<input type="hidden" name="id" value={changelog.id} />
43-
<TitleSlug defaultSlug={changelog.slug} defaultTitle={changelog.title} />
44-
<FileUpload defaultFile={changelog.image || ''} />
45-
<div className="my-6">
46-
<label htmlFor="summary" className="block text-xs font-medium text-gray-700">
47-
Summary
48-
<Fragment>
49-
&nbsp;<span className="font-bold text-secondary">*</span>
50-
</Fragment>
51-
</label>
52-
<textarea name="summary" className="w-full" required>
53-
{changelog.summary}
54-
</textarea>
55-
<span className="text-xs text-gray-500 italic">
56-
This will be shown in the list
57-
</span>
58-
</div>
59-
<div>
60-
<Select
61-
name="categories"
62-
className="mt-1 mb-6"
63-
label="Category"
64-
placeholder="Select Category"
65-
defaultValue={changelog.categories.map(category => ({
66-
label: category.name,
67-
value: category.name,
68-
}))}
69-
options={categories.map(category => ({
70-
label: category.name,
71-
value: category.name,
72-
}))}
73-
isMulti
74-
/>
75-
</div>
76-
77-
<Suspense fallback={null}>
78-
<ForwardRefEditor
79-
name="content"
80-
defaultValue={changelog.content || ''}
81-
className="w-full"
82-
/>
83-
</Suspense>
84-
85-
<footer className="flex items-center justify-between mt-2 mb-8">
86-
<Link href="/changelog/_admin" className="underline text-gray-500">
87-
Return to Changelogs list
88-
</Link>
89-
<div>
90-
<Button type="submit">Update</Button>
91-
</div>
92-
</footer>
93-
</form>
36+
<EditChangelogForm changelog={changelog} categories={categories} />
9437
</section>
9538
);
9639
}

apps/changelog/src/app/changelog/%5Fadmin/confirm.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
'use client';
22

3-
import type {PropsWithChildren} from 'react';
3+
import {ServerActionPayloadInterface} from '@/server/actions/serverActionPayload.interface';
4+
import {useActionState, type PropsWithChildren} from 'react';
45

56
export default function Confirm({
67
action,
78
changelog,
89
children,
910
}: PropsWithChildren<{
10-
action: (formData: FormData) => Promise<void | {
11-
message: string;
12-
}>;
11+
action: (
12+
currentFormState: ServerActionPayloadInterface,
13+
formData: FormData
14+
) => Promise<ServerActionPayloadInterface>;
1315
changelog: {id: string};
1416
}>) {
17+
const [_state, formAction] = useActionState(action, {});
1518
return (
1619
<form
17-
action={action}
20+
action={formAction}
1821
className="inline-block"
1922
onSubmit={e => {
2023
e.preventDefault();
2124
// eslint-disable-next-line no-alert
2225
if (confirm('Are you sure?')) {
23-
action(new FormData(e.currentTarget));
26+
formAction(new FormData(e.currentTarget));
2427
}
2528
}}
2629
>
Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
import {Fragment} from 'react';
2-
import Link from 'next/link';
31
import {prismaClient} from '@/server/prisma-client';
4-
import {createChangelog} from '@/server/actions/changelog';
5-
import {TitleSlug} from '@/client/components/titleSlug';
6-
import {FileUpload} from '@/client/components/fileUpload';
7-
import {Select} from '@/client/components/ui/Select';
8-
import {ForwardRefEditor} from '@/client/components/forwardRefEditor';
9-
import {Button} from '@/client/components/ui/Button';
2+
103
import {getServerSession} from 'next-auth/next';
114
import {notFound} from 'next/navigation';
125
import {authOptions} from '@/server/authOptions';
6+
import {CreateChangelogForm} from '@/client/components/forms/createChangelogForm';
137

148
export default async function ChangelogCreatePage() {
159
const session = await getServerSession(authOptions);
@@ -26,48 +20,7 @@ export default async function ChangelogCreatePage() {
2620

2721
return (
2822
<section className="overflow-x-auto col-start-3 col-span-8">
29-
<form action={createChangelog} className="px-2 w-full">
30-
<TitleSlug />
31-
<FileUpload />
32-
<div className="my-6">
33-
<label htmlFor="summary" className="block text-xs font-medium text-gray-700">
34-
Summary
35-
<Fragment>
36-
&nbsp;<span className="font-bold text-secondary">*</span>
37-
</Fragment>
38-
</label>
39-
<textarea name="summary" className="w-full" required />
40-
<span className="text-xs text-gray-500 italic">
41-
This will be shown in the list
42-
</span>
43-
</div>
44-
<div>
45-
<Select
46-
name="categories"
47-
className="mt-1 mb-6"
48-
label="Category"
49-
placeholder="Select Category"
50-
options={categories.map(category => ({
51-
label: category.name,
52-
value: category.name,
53-
}))}
54-
isMulti
55-
/>
56-
</div>
57-
58-
<ForwardRefEditor name="content" className="w-full" />
59-
60-
<footer className="flex items-center justify-between mt-2">
61-
<Link href="/changelog/_admin" className="underline text-gray-500">
62-
Return to Changelogs list
63-
</Link>
64-
<div>
65-
<Button type="submit">Create (not published yet)</Button>
66-
<br />
67-
<span className="text-xs text-gray-500 italic">You can publish it later</span>
68-
</div>
69-
</footer>
70-
</form>
23+
<CreateChangelogForm categories={categories} />
7124
</section>
7225
);
7326
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use client';
2+
3+
import {createChangelog} from '@/server/actions/changelog';
4+
import {TitleSlug} from '@/client/components/titleSlug';
5+
import {FileUpload} from '@/client/components/fileUpload';
6+
import {Select} from '@/client/components/ui/Select';
7+
import {ForwardRefEditor} from '@/client/components/forwardRefEditor';
8+
import {Button} from '@/client/components/ui/Button';
9+
import {Fragment, useActionState} from 'react';
10+
import Link from 'next/link';
11+
import {Category} from '@prisma/client';
12+
13+
export const CreateChangelogForm = ({categories}: {categories: Category[]}) => {
14+
const [_state, formAction] = useActionState(createChangelog, {});
15+
return (
16+
<form action={formAction} className="px-2 w-full">
17+
<TitleSlug />
18+
<FileUpload />
19+
<div className="my-6">
20+
<label htmlFor="summary" className="block text-xs font-medium text-gray-700">
21+
Summary
22+
<Fragment>
23+
&nbsp;<span className="font-bold text-secondary">*</span>
24+
</Fragment>
25+
</label>
26+
<textarea name="summary" className="w-full" required />
27+
<span className="text-xs text-gray-500 italic">
28+
This will be shown in the list
29+
</span>
30+
</div>
31+
<div>
32+
<Select
33+
name="categories"
34+
className="mt-1 mb-6"
35+
label="Category"
36+
placeholder="Select Category"
37+
options={categories.map(category => ({
38+
label: category.name,
39+
value: category.name,
40+
}))}
41+
isMulti
42+
/>
43+
</div>
44+
45+
<ForwardRefEditor name="content" className="w-full" />
46+
47+
<footer className="flex items-center justify-between mt-2">
48+
<Link href="/changelog/_admin" className="underline text-gray-500">
49+
Return to Changelogs list
50+
</Link>
51+
<div>
52+
<Button type="submit">Create (not published yet)</Button>
53+
<br />
54+
<span className="text-xs text-gray-500 italic">You can publish it later</span>
55+
</div>
56+
</footer>
57+
</form>
58+
);
59+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
'use client';
2+
3+
import {editChangelog} from '@/server/actions/changelog';
4+
import {TitleSlug} from '@/client/components/titleSlug';
5+
import {FileUpload} from '@/client/components/fileUpload';
6+
import {Select} from '@/client/components/ui/Select';
7+
import {ForwardRefEditor} from '@/client/components/forwardRefEditor';
8+
import {Button} from '@/client/components/ui/Button';
9+
import {Fragment, Suspense, useActionState} from 'react';
10+
import {Changelog, Category} from '@prisma/client';
11+
import Link from 'next/link';
12+
13+
export const EditChangelogForm = ({
14+
changelog,
15+
categories,
16+
}: {
17+
changelog: Changelog;
18+
categories: Category[];
19+
}) => {
20+
const [_state, formAction] = useActionState(editChangelog, {});
21+
return (
22+
<form action={formAction} className="px-2 w-full">
23+
<input type="hidden" name="id" value={changelog.id} />
24+
<TitleSlug defaultSlug={changelog.slug} defaultTitle={changelog.title} />
25+
<FileUpload defaultFile={changelog.image || ''} />
26+
<div className="my-6">
27+
<label htmlFor="summary" className="block text-xs font-medium text-gray-700">
28+
Summary
29+
<Fragment>
30+
&nbsp;<span className="font-bold text-secondary">*</span>
31+
</Fragment>
32+
</label>
33+
<textarea name="summary" className="w-full" required>
34+
{changelog.summary}
35+
</textarea>
36+
<span className="text-xs text-gray-500 italic">
37+
This will be shown in the list
38+
</span>
39+
</div>
40+
<div>
41+
<Select
42+
name="categories"
43+
className="mt-1 mb-6"
44+
label="Category"
45+
placeholder="Select Category"
46+
defaultValue={categories.map(category => ({
47+
label: category.name,
48+
value: category.name,
49+
}))}
50+
options={categories.map(category => ({
51+
label: category.name,
52+
value: category.name,
53+
}))}
54+
isMulti
55+
/>
56+
</div>
57+
58+
<Suspense fallback={null}>
59+
<ForwardRefEditor
60+
name="content"
61+
defaultValue={changelog.content || ''}
62+
className="w-full"
63+
/>
64+
</Suspense>
65+
66+
<footer className="flex items-center justify-between mt-2 mb-8">
67+
<Link href="/changelog/_admin" className="underline text-gray-500">
68+
Return to Changelogs list
69+
</Link>
70+
<div>
71+
<Button type="submit">Update</Button>
72+
</div>
73+
</footer>
74+
</form>
75+
);
76+
};

0 commit comments

Comments
 (0)