(
action: ActionableClient<TInput, TOutput, TError>,
options: UseServerActionOptions<TInput, TOutput, UnactionableError<TError>> = {},
)
| 102 | * @see {@link https://orpc.dev/docs/server-action#useserveraction-hook Server Action Hook Docs} |
| 103 | */ |
| 104 | export function useServerAction<TInput, TOutput, TError extends ORPCErrorJSON<any, any>>( |
| 105 | action: ActionableClient<TInput, TOutput, TError>, |
| 106 | options: UseServerActionOptions<TInput, TOutput, UnactionableError<TError>> = {}, |
| 107 | ): UseServerActionResult<TInput, TOutput, UnactionableError<TError>> { |
| 108 | const [state, setState] = useState<Omit< |
| 109 | | UseServerActionIdleResult<TInput, TOutput, UnactionableError<TError>> |
| 110 | | UseServerActionSuccessResult<TInput, TOutput, UnactionableError<TError>> |
| 111 | | UseServerActionErrorResult<TInput, TOutput, UnactionableError<TError>>, |
| 112 | keyof UseServerActionResultBase<TInput, TOutput, UnactionableError<TError>> | 'executedAt' | 'input' |
| 113 | >>(INITIAL_STATE) |
| 114 | |
| 115 | const executedAtRef = useRef<Date | undefined>(undefined) |
| 116 | const [input, setInput] = useState<TInput | undefined>(undefined) |
| 117 | const [isPending, startTransition] = useTransition() |
| 118 | |
| 119 | const reset = useCallback(() => { |
| 120 | executedAtRef.current = undefined |
| 121 | setInput(undefined) |
| 122 | setState({ ...INITIAL_STATE }) |
| 123 | }, []) |
| 124 | |
| 125 | const execute = useCallback(async (input: TInput, executeOptions: UseServerActionExecuteOptions<TInput, TOutput, UnactionableError<TError>> = {}) => { |
| 126 | const executedAt = new Date() |
| 127 | executedAtRef.current = executedAt |
| 128 | |
| 129 | setInput(input) |
| 130 | |
| 131 | return new Promise((resolve) => { |
| 132 | startTransition(async () => { |
| 133 | const result = await safe(intercept( |
| 134 | [...toArray(options.interceptors), ...toArray(executeOptions.interceptors)], |
| 135 | { input: input as TInput }, |
| 136 | async ({ input }) => action(input).then(([error, data]) => { |
| 137 | if (error) { |
| 138 | throw createORPCErrorFromJson(error) |
| 139 | } |
| 140 | |
| 141 | return data as TOutput |
| 142 | }), |
| 143 | )) |
| 144 | |
| 145 | /** |
| 146 | * If multiple execute calls are made in parallel, only the last one will be effective. |
| 147 | */ |
| 148 | if (executedAtRef.current === executedAt) { |
| 149 | setState({ |
| 150 | data: result.data, |
| 151 | error: result.error as any, |
| 152 | isIdle: false, |
| 153 | isPending: false, |
| 154 | isSuccess: !result.error, |
| 155 | isError: !!result.error, |
| 156 | status: !result.error ? 'success' : 'error', |
| 157 | }) |
| 158 | } |
| 159 | |
| 160 | resolve(result) |
| 161 | }) |
no test coverage detected