Skip to content

Commit

Permalink
feat: support stateful actions via stateAction method and `useState…
Browse files Browse the repository at this point in the history
…Action` hook (#110)

This PR adds support for stateful actions (used for React `useActionState` hook) via the `stateAction` method, and the `useStateAction` hook.
  • Loading branch information
TheEdoRan committed May 7, 2024
1 parent 242dbf8 commit d060132
Show file tree
Hide file tree
Showing 78 changed files with 9,160 additions and 6,001 deletions.
132 changes: 66 additions & 66 deletions .github/ISSUE_TEMPLATE/1_bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,71 @@ description: Create a report to help us fix bugs.
title: "[BUG] "
labels: ["bug"]
body:
- type: checkboxes
attributes:
label: Are you using the latest version of this library?
description: Please confirm that you are using the latest version of next-safe-action.
options:
- label: I verified that the issue exists in the latest next-safe-action release
- type: checkboxes
attributes:
label: Are you using the latest version of this library?
description: Please confirm that you are using the latest version of next-safe-action.
options:
- label: I verified that the issue exists in the latest next-safe-action release
required: true
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues and found nothing that matches
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
placeholder: I found out that the '...' functionality is not working.
validations:
required: true
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues and found nothing that matches
- type: textarea
attributes:
label: Reproduction steps
description: Steps to reproduce the incorrect behavior.
placeholder: |
A step-by-step reproduction of the bug. For example:
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
placeholder: I found out that the '...' functionality is not working.
validations:
required: true
- type: textarea
attributes:
label: Reproduction steps
description: Steps to reproduce the incorrect behavior.
placeholder: |
A step-by-step reproduction of the bug. For example:
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A description of what you expected to happen instead.
placeholder: I expected that this happened instead.
validations:
required: true
- type: input
attributes:
label: Reproduction example
description: Link to an example that reproduces the bug. Could be, for example, a GitHub repo or a CodeSandbox link.
placeholder: https://github.com/...
validations:
required: true
- type: markdown
attributes:
value: Information about the environment you are using.
- type: input
attributes:
label: Operating System
placeholder: Windows 11, macOS, Ubuntu 22.04
validations:
required: true
- type: input
attributes:
label: Library version
placeholder: 6.0.0
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context about the problem here.
validations:
required: false
- type: textarea
attributes:
label: Expected behavior
description: A description of what you expected to happen instead.
placeholder: I expected that this happened instead.
validations:
required: true
- type: input
attributes:
label: Reproduction example
description: Link to an example that reproduces the bug. Could be, for example, a GitHub repo or a CodeSandbox link.
placeholder: https://github.com/...
validations:
required: true
- type: markdown
attributes:
value: Information about the environment you are using.
- type: input
attributes:
label: Operating System
placeholder: Windows 11, macOS, Ubuntu 22.04
validations:
required: true
- type: input
attributes:
label: Library version
placeholder: 6.0.0
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context about the problem here.
validations:
required: false
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ contact_links:
about: Do you want to help us improve the library? Open a new discussion to suggest an idea.
- name: Ask a question
url: https://github.com/TheEdoRan/next-safe-action/discussions/new?category=q-a
about: Do you want to ask something about the library? Open a new discussion to get help from the community.
about: Do you want to ask something about the library? Open a new discussion to get help from the community.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ yarn-error.log*
# Turborepo
.turbo

# Local test file
/packages/next-safe-action/src/test.ts

# Website
/website/.docusaurus
/website/node_modules
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
},
"editor.rulers": [120],
"[markdown]": {
"editor.formatOnSave": false
}
}
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ After forking, cloning the repository and optionally creating a new branch from
pnpm install
```

Then, you can run the `rebuild:lib` command to rebuild the library code, and then test it in the playground app:
Then, you can run the `build:lib` command to rebuild the library code, and then test it in the playground app:

```sh
pnpm run rebuild:lib && pnpm run playground
pnpm run build:lib && pnpm run pg
```

> [!TIP]
> If you see many type errors in the playground app after running the `rebuild:lib` command, try to restart the TS Server of VS Code. This should fix the errors.
> If you see many type errors in the playground app after running the `build:lib` command, try to restart the TS Server of VS Code. This should fix the errors.
If you updated user facing APIs of the library, you're **not required**, but **highly incouraged** to update [the documentation](../website/docs) of the library to reflect the changes you've made. This can be done in later stages of the PR too, for instance when a maintainer already approved your code updates.

Expand Down
4 changes: 2 additions & 2 deletions apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@hookform/resolvers": "^3.3.4",
"@typeschema/zod": "^0.13.3",
"lucide-react": "^0.372.0",
"next": "14.2.2",
"next": "14.3.0-canary.42",
"next-safe-action": "workspace:*",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand All @@ -27,7 +27,7 @@
"@types/react-dom": "18.2.25",
"autoprefixer": "10.4.19",
"eslint": "^8.57.0",
"eslint-config-next": "14.2.2",
"eslint-config-next": "14.3.0-canary.42",
"postcss": "8.4.38",
"tailwindcss": "3.4.3",
"typescript": "^5.4.5"
Expand Down
10 changes: 5 additions & 5 deletions apps/playground/postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
15 changes: 3 additions & 12 deletions apps/playground/src/app/(examples)/bind-arguments/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,14 @@ export default function BindArguments() {
);

const { execute, result, status, reset } = useAction(boundOnboardUser, {
onSuccess({ data, input, reset }) {
onSuccess({ data, input }) {
console.log("HELLO FROM ONSUCCESS", data, input);

// You can reset result object by calling `reset`.
// reset();
},
onError({ error, input, reset }) {
onError({ error, input }) {
console.log("OH NO FROM ONERROR", error, input);

// You can reset result object by calling `reset`.
// reset();
},
onSettled({ result, input, reset }) {
onSettled({ result, input }) {
console.log("HELLO FROM ONSETTLED", result, input);

// You can reset result object by calling `reset`.
// reset();
},
onExecute({ input }) {
console.log("HELLO FROM ONEXECUTE", input);
Expand Down
27 changes: 0 additions & 27 deletions apps/playground/src/app/(examples)/client-form/page.tsx

This file was deleted.

12 changes: 0 additions & 12 deletions apps/playground/src/app/(examples)/client-form/signup-action.ts

This file was deleted.

15 changes: 15 additions & 0 deletions apps/playground/src/app/(examples)/empty-response/empty-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use server";

import { action } from "@/lib/safe-action";
import { z } from "zod";

const schema = z.object({
userId: z.string().uuid(),
});

export const emptyAction = action
.metadata({ actionName: "emptyAction" })
.schema(schema)
.action(async () => {
await new Promise((res) => setTimeout(res, 500));
});
44 changes: 44 additions & 0 deletions apps/playground/src/app/(examples)/empty-response/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { StyledButton } from "@/app/_components/styled-button";
import { StyledHeading } from "@/app/_components/styled-heading";
import { useAction } from "next-safe-action/hooks";
import { ResultBox } from "../../_components/result-box";
import { emptyAction } from "./empty-action";

export default function EmptyResponse() {
const { execute, result, status, reset } = useAction(emptyAction, {
onSuccess({ data, input }) {
console.log("HELLO FROM ONSUCCESS", data, input);
},
onError({ error, input }) {
console.log("OH NO FROM ONERROR", error, input);
},
onSettled({ result, input }) {
console.log("HELLO FROM ONSETTLED", result, input);
},
onExecute({ input }) {
console.log("HELLO FROM ONEXECUTE", input);
},
});

console.log("status:", status);

return (
<main className="w-96 max-w-full px-4">
<StyledHeading>Action without response data</StyledHeading>
<StyledButton
type="button"
className="mt-4"
onClick={() => {
execute({ userId: crypto.randomUUID() });
}}>
Execute action
</StyledButton>
<StyledButton className="mt-4" type="button" onClick={reset}>
Reset
</StyledButton>
<ResultBox result={result} status={status} />
</main>
);
}
15 changes: 3 additions & 12 deletions apps/playground/src/app/(examples)/hook/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,14 @@ import { deleteUser } from "./deleteuser-action";
export default function Hook() {
// Safe action (`deleteUser`) and optional callbacks passed to `useAction` hook.
const { execute, result, status, reset } = useAction(deleteUser, {
onSuccess({ data, input, reset }) {
onSuccess({ data, input }) {
console.log("HELLO FROM ONSUCCESS", data, input);

// You can reset result object by calling `reset`.
// reset();
},
onError({ error, input, reset }) {
onError({ error, input }) {
console.log("OH NO FROM ONERROR", error, input);

// You can reset result object by calling `reset`.
// reset();
},
onSettled({ result, input, reset }) {
onSettled({ result, input }) {
console.log("HELLO FROM ONSETTLED", result, input);

// You can reset result object by calling `reset`.
// reset();
},
onExecute({ input }) {
console.log("HELLO FROM ONEXECUTE", input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

import { action } from "@/lib/safe-action";

export const emptyAction = action
.metadata({ actionName: "onboardUser" })
.action(async (obj) => {
console.log("OBJ ->", obj);

export const noargsAction = action
.metadata({ actionName: "noargsAction" })
.action(async () => {
await new Promise((res) => setTimeout(res, 500));

return {
Expand Down

0 comments on commit d060132

Please sign in to comment.