Skip to content

06 Forms

JP Barbosa edited this page Apr 18, 2021 · 2 revisions

Forms

Components

Components

Create useForm hook

code ./src/hooks/useForm.ts
import { useState } from 'react';

export type IChangeElement =
  | HTMLInputElement
  | HTMLSelectElement
  | HTMLTextAreaElement;

export const useForm = <T>(initialState: T) => {
  const [formState, setFormState] = useState<T>(initialState);

  const handleChange = (event: React.ChangeEvent<IChangeElement>): void => {
    const { tagName, name, value } = event.target;
    const parsedValue = tagName === 'SELECT' && value === '' ? null : value;
    setFormState({ ...formState, [name]: parsedValue });
  };

  return { formState, setFormState, handleChange };
};

Add ArticleFormFields and emptyRecord to ArticleIndex

code ./src/pages/Article/index.tsx
...
import { ArticleFormFields } from './FormFields';

export const ArticleIndex: React.FC = () => {
  ...

  const emptyRecord = {
    title: '',
    text: '',
  };

  return (
    <RecordIndex<Article>
      ...
      FormFields={ArticleFormFields}
      emptyRecord={emptyRecord}
    />
  );
}

Add FormFields and emptyRecord to RecordIndexProps

code ./src/interfaces/PagesProps.ts
export interface RecordIndexProps<T> {
  ...
  FormFields: React.FC<FormFieldsProps<T>>;
  emptyRecord: T;
}

...

Add RecordMutations to RecordIndex

code ./src/pages/Record/index.tsx
...
import { RecordMutations } from './Mutations';

export const RecordIndex = <T extends Record>({
  ...
  FormFields,
  emptyRecord,
}: RecordIndexProps<T>) => {
  ...

  return (
    <div className="page">
      <div className="content">
        ...
        <RecordMutations<T>
          FormFields={FormFields}
          activeRecord={emptyRecord}
        />
      </div>
    </div>
  );
};

Create RecordMutations Props

code ./src/interfaces/PagesProps.ts
...

export interface RecordMutationsProps<T> {
  FormFields: React.FC<FormFieldsProps<T>>;
  activeRecord: T;
}

Create RecordMutations

code ./src/pages/Record/Mutations.tsx
import { Record } from '../../interfaces/RecordEntities';
import { RecordMutationsProps } from '../../interfaces/PagesProps';
import { RecordNew } from './New';

export const RecordMutations = <T extends Record>({
  FormFields,
  activeRecord,
}: RecordMutationsProps<T>) => {
  return (
    <div className="mutations">
      <RecordNew<T> FormFields={FormFields} activeRecord={activeRecord} />
    </div>
  );
};

Specify RecordNewProps

code ./src/interfaces/PagesProps.ts
...

export interface RecordNewProps<T> {
  FormFields: React.FC<FormFieldsProps<T>>;
  activeRecord: T;
}

Create RecordNew

code ./src/pages/Record/New.tsx
import { Record } from '../../interfaces/RecordEntities';
import { RecordNewProps } from '../../interfaces/PagesProps';
import { RecordForm } from './Form';

export const RecordNew = <T extends Record>({
  FormFields,
  activeRecord,
}: RecordNewProps<T>) => {
  return (
    <div className="new">
      <h2>New</h2>
      <RecordForm FormFields={FormFields} activeRecord={activeRecord} />
    </div>
  );
};

Specify RecordFormProps

code ./src/interfaces/PagesProps.ts
...

export interface RecordFormProps<T> {
  FormFields: React.FC<FormFieldsProps<T>>;
  activeRecord: T;
}

Create RecordForm

code ./src/pages/Record/Form.tsx
import { Record } from '../../interfaces/RecordEntities';
import { RecordFormProps } from '../../interfaces/PagesProps';
import { useForm } from '../../hooks/useForm';

export const RecordForm = <T extends Record>({
  FormFields,
  activeRecord,
}: RecordFormProps<T>) => {
  const { formState, handleChange } = useForm<T>(activeRecord);

  return (
    <div>
      <form>
        <FormFields formState={formState} handleChange={handleChange} />
        <input type="submit" />
      </form>
    </div>
  );
};

Specify FormFieldsProps

code ./src/interfaces/PagesProps.ts
import { IChangeElement } from '../hooks/useForm';

...

export interface FormFieldsProps<T> {
  formState: T;
  handleChange: (event: React.ChangeEvent<IChangeElement>) => void;
}

Create ArticleFormFields

code ./src/pages/Article/FormFields.tsx
import { useFetch } from '../../hooks/useFetch';
import { FormFieldsProps } from '../../interfaces/PagesProps';
import { Article, Author } from '../../interfaces/RecordEntities';

type IProps = FormFieldsProps<Article>;

export const ArticleFormFields: React.FC<IProps> = ({
  formState,
  handleChange,
}) => {
  const authorsFetch = useFetch<Author>('authors');

  return (
    <div>
      <div>
        <label>Title</label>
        <input
          type="text"
          name="title"
          value={formState.title}
          onChange={handleChange}
        />
      </div>
      <div>
        <label>Text</label>
        <textarea
          name="text"
          value={formState.text}
          onChange={handleChange}
          rows={5}
        />
      </div>
      <div>
        <label>Author</label>
        <select
          name="author"
          value={formState.author ? formState.author.id : ''}
          onChange={handleChange}
        >
          <option value="">Select</option>
          {authorsFetch.records.map((author) => (
            <option key={author.id} value={author.id}>
              {author.name}
            </option>
          ))}
        </select>
      </div>
    </div>
  );
};

Result

Result

Commit

git add .
git commit -m "Forms"

Next step: Active Record