Use forms with or without client-side JavaScript--no code duplication required!
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

90 lines
2.2 KiB

  1. import * as React from 'react';
  2. import fetchPonyfill from 'fetch-ponyfill';
  3. import { EncTypeSerializerMap, serializeBody, SerializerOptions } from '../../utils/serialization';
  4. import { ENCTYPE_MULTIPART_FORM_DATA } from '../../common';
  5. import { AllowedClientMethod, FormDerivedElement } from '../common';
  6. export interface UseFormFetchParams {
  7. clientAction?: string;
  8. clientHeaders?: HeadersInit;
  9. clientMethod: AllowedClientMethod;
  10. encType: string;
  11. invalidate?: (...args: unknown[]) => unknown;
  12. refresh?: (response: Response) => void;
  13. encTypeSerializers?: EncTypeSerializerMap;
  14. responseEncType: string;
  15. serializerOptions?: SerializerOptions;
  16. onSubmit?: React.FormEventHandler<FormDerivedElement>;
  17. }
  18. export const useFormFetch = ({
  19. clientAction,
  20. invalidate,
  21. clientHeaders,
  22. responseEncType,
  23. encType,
  24. clientMethod,
  25. encTypeSerializers,
  26. serializerOptions,
  27. refresh,
  28. onSubmit,
  29. }: UseFormFetchParams) => {
  30. const handleSubmit: React.FormEventHandler<
  31. FormDerivedElement
  32. > = React.useCallback(async (event) => {
  33. event.preventDefault();
  34. const { submitter } = event.nativeEvent as unknown as { submitter?: HTMLElement };
  35. if (clientAction) {
  36. invalidate?.();
  37. const { fetch } = fetchPonyfill();
  38. const headers: HeadersInit = {
  39. ...(clientHeaders ?? {}),
  40. Accept: responseEncType,
  41. };
  42. if (encType !== ENCTYPE_MULTIPART_FORM_DATA) {
  43. // browser automatically generates content-type header for multipart/form-data
  44. (headers as unknown as Record<string, string>)['Content-Type'] = encType;
  45. }
  46. const fetchInit: RequestInit = {
  47. method: clientMethod.toUpperCase(),
  48. headers,
  49. };
  50. if (!['GET', 'HEAD'].includes(clientMethod.toUpperCase())) {
  51. fetchInit.body = serializeBody({
  52. form: event.currentTarget,
  53. encType,
  54. serializers: encTypeSerializers,
  55. options: {
  56. ...serializerOptions,
  57. submitter,
  58. },
  59. });
  60. }
  61. const response = await fetch(clientAction, fetchInit);
  62. refresh?.(response);
  63. }
  64. onSubmit?.(event);
  65. }, [
  66. clientAction,
  67. invalidate,
  68. clientHeaders,
  69. responseEncType,
  70. encType,
  71. clientMethod,
  72. encTypeSerializers,
  73. serializerOptions,
  74. refresh,
  75. onSubmit,
  76. ]);
  77. return React.useMemo(() => ({
  78. handleSubmit,
  79. }), [
  80. handleSubmit,
  81. ]);
  82. };