Проверьте, изменено ли поле ввода

Я делаю шаговую форму с помощью React. И структура формы почти готова ..

Есть два шага,

-> Basic Details

-> Employment Details

Здесь контекст формы использовался для заполнения значения по умолчанию поля ввода, а также, если были внесены какие-либо изменения, это сохраняется через состояние.

form-context.js

import React, { useState } from 'react';

export const FormContext = React.createContext();

export function FormProvider({ children }) {
  const [formValue, setFormValue] = useState({
    basicDetails: {
      firstName: 'John',
      lastName: '',
    },
    companyDetails: {
      companyName: 'Some Company',
      designation: 'Some Designation',
    },
  });

  return (
    <FormContext.Provider value={[formValue, setFormValue]}>
      {children}
    </FormContext.Provider>
  );
}

Здесь у меня есть следующие и предыдущие кнопки для перехода между шагами,

index.js:

  const next = () => setCurrentPage((prev) => prev + 1);
  const prev = () => setCurrentPage((prev) => prev - 1);

Требование:

- ›При нажатии кнопки next/previous мне нужно проверить, изменен ли какой-либо ввод.

- ›Благодаря этому я могу вызвать API, чтобы сохранить изменения при нажатии кнопки« Далее »в моем реальном приложении.

- ›Вот, если вы поможете мне сделать console log, этого будет более чем достаточно.

Например:

- ›Если пользователь изменяет имя в разделе основных деталей с John на Doe, то при нажатии следующей кнопки можно будет консоль зарегистрировать в журнале, что есть изменения в основных деталях.

- ›Если ни одно из полей не было изменено в разделе основных деталей, можно сразу перейти к следующему шагу (как сейчас).

Примечание. Не используйте жесткое кодирование имени ввода, потому что у меня более 30 полей ввода для каждого шага.

 Редактировать следующий-динамический-тест-вопрос (разветвленный)


person Undefined    schedule 16.11.2020    source источник


Ответы (1)


Я думаю, что основная проблема здесь в том, что ваша текущая реализация заменяет существующее состояние, при каждом изменении, поэтому невозможно узнать, изменилось ли / что. Здесь не сработает даже перехватчик usePrevious для хранения предыдущего значения состояния, так как он будет удерживать для сравнения только последнее предыдущее редактирование поля, а не исходное значение. Многие решения для обработки форм обрабатывают это, сохраняя исходное состояние, которое не изменено, или отслеживая грязные поля и / или многое другое.

Вот мое предложение по настройке состояния контекста вашей формы для отслеживания обновленных полей. Объект dirtyFields содержит исходное значение.

FormProvider

const [formValue, setFormValue] = useState({
  basicDetails: {
    fields: {
      firstName: 'John',
      lastName: '',
    },
    dirtyFields: {},
  },
  companyDetails: {
    fields: {
      companyName: 'Some Company',
      designation: 'Some Designation',
    },
    dirtyFields: {},
  },
});

Основные сведения

const BasicDetails = () => {
  const [value, setValue] = React.useContext(FormContext);
  const {
    basicDetails: { fields }, // <-- get field values
  } = value;

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    setValue((prev) => ({
      ...prev,
      basicDetails: {
        ...prev.basicDetails,
        fields: {
          ...prev.basicDetails.fields,
          [name]: value,
        },
        ...(prev.basicDetails.dirtyFields[name] // <-- only mark dirty once with original value
          ? {}
          : {
              dirtyFields: {
                ...prev.basicDetails.dirtyFields,
                [name]: prev.basicDetails.fields[name],
              },
            }),
      },
    }));
  };

  return (
    <>
      <div className="form-group col-sm-6">
        <label htmlFor="firstName">First Name</label>
        <input
          ...
          value={fields.firstName} // <-- access fields object
          ...
        />
      </div>
      <div className="form-group col-sm-4">
        <label htmlFor="lastName">Last Name</label>
        <input
          ...
          value={fields.lastName}
          ...
        />
      </div>
    </>
  );
};

ТрудоустройствоПодробнее

const EmploymentDetails = () => {
  const [value, setValue] = React.useContext(FormContext);
  const {
    companyDetails: { fields },
  } = value;

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    setValue((prev) => ({
      ...prev,
      companyDetails: {
        ...prev.companyDetails,
        fields: {
          ...prev.companyDetails.fields,
          [name]: value,
        },
        ...(prev.companyDetails.dirtyFields[name]
          ? {}
          : {
              dirtyFields: {
                ...prev.companyDetails.dirtyFields,
                [name]: prev.companyDetails.fields[name],
              },
            }),
      },
    }));
  };

  return (
    <>
      <div className="form-group col-sm-6">
        <label htmlFor="companyName">Company Name</label>
        <input
          ...
          value={fields.companyName}
          ...
        />
      </div>
      <div className="form-group col-sm-4">
        <label htmlFor="designation">Designation</label>
        <input
          ...
          value={fields.designation}
          ...
        />
      </div>
    </>
  );
};

Проверяйте грязные поля при увеличении / уменьшении шага.

Дайте каждому разделу идентификатор, соответствующий ключу формы в контексте формы.

const sections = [
  {
    title: 'Basic Details',
    id: 'basicDetails',
    onClick: () => setCurrentPage(1),
  },
  {
    title: 'Employment Details',
    id: 'companyDetails',
    onClick: () => setCurrentPage(2),
  },
  { title: 'Review', id: 'review', onClick: () => setCurrentPage(3) },
];

Создайте checkDirty утилиту. Здесь я просто регистрирую грязные поля

const checkDirty = (page) => {
  console.log('check dirty', 'page', page);
  console.log(
    value[sections[page - 1].id] && value[sections[page - 1].id].dirtyFields,
  );
};

const next = () => {
  setCurrentPage((prev) => prev + 1);
  checkDirty(currentPage); // <-- check for dirty fields when updating page step
};

const prev = () => {
  setCurrentPage((prev) => prev - 1);
  checkDirty(currentPage);
};

Из-за дополнительного вложенного состояния в контексте формы здесь есть служебная программа, которая сокращает его обратно до только данных формы, которые вы хотите отобразить на этапе проверки.

const prettyReview = sections.reduce(
  (sections, section) => ({
    ...sections,
    ...(value[section.id]
      ? { [section.id]: { ...value[section.id].fields } }
      : {}),
  }),
  {},
);

...

<pre>{JSON.stringify(prettyReview, null, 2)}</pre>

 Изменить check-if-input-field-is-changed

Редактировать

Вы сказали, что ваши данные поступают из вызова API серверной части. Вот функция инициализации состояния контекста, которая сопоставляет форму данных API с формой состояния, которая у меня есть.

Данные из API

const apiData = {
  basicDetails: {
    firstName: 'John',
    lastName: '',
  },
  companyDetails: {
    companyName: 'Some Company',
    designation: 'Some Designation',
  },
};

Функция инициализации

const initializeContext = (data) =>
  Object.entries(data).reduce(
    (sections, [section, fields]) => ({
      ...sections,
      [section]: {
        fields,
        dirtyFields: {},
      },
    }),
    {},
  );

Инициализировать FormProvider состояние контекста

function FormProvider({ children }) {
  const [formValue, setFormValue] = useState(initializeContext(apiData));

  return (
    <FormContext.Provider value={[formValue, setFormValue]}>
      {children}
    </FormContext.Provider>
  );
}
person Drew Reese    schedule 16.11.2020
comment
Спасибо за подробный ответ. Я приму ваше решение. У вас есть коды и ящик для той же реализации? - person Undefined; 17.11.2020
comment
@Undefined Я думал, что нет, но я сохранил его в моей учетной записи, поэтому я, должно быть, намеревался и просто забыл добавить ссылку на него в своем ответе. Я вот это добавил. - person Drew Reese; 17.11.2020
comment
О, теперь я понял, почему это не работает в моем реальном приложении. В моем приложении эти значения JSON для формы поступают из внутреннего API, поэтому я не могу изменить значение JSON, как это сделали вы. Можете ли вы здесь чем-нибудь помочь? Извините, что я не указал на это за ошибку .. Потому что думал, что решение будет прямым .. - person Undefined; 17.11.2020
comment
@Undefined А, я понимаю ... ну, вы можете создать новый объект из объекта данных серверной части, в основном неглубоко скопировать его в новую форму и добавить грязные свойства. Предполагая, что ваши внутренние данные являются текущей формой из вашего вопроса, я могу обновить свой ответ завтра утром с помощью функции сопоставления (если вам это нужно). - person Drew Reese; 17.11.2020
comment
Да, Дрю, пожалуйста, добавьте свое новое решение к своему утру .. Я постараюсь реализовать и это. На самом деле вы очень помогли, и спасибо за это .. - person Undefined; 17.11.2020
comment
@Undefined Обновленный ответ с функцией инициализации для отображения формы данных. Это должно приблизить вас к тому, что вы ищете. - person Drew Reese; 17.11.2020
comment
Большое спасибо, Дрю Риз, цените вашу сердечную помощь .. Это работает для меня .. - person Undefined; 17.11.2020
comment
Братан, ты можешь помочь мне в вопросе, в котором я взял решение из твоего предыдущего ответа, но оно не работает .. Вопрос здесь: stackoverflow. com / q / 64967082/13270726 - person Undefined; 23.11.2020
comment
У вас была возможность помочь мне в данном вопросе? Жду вашего решения со вчерашнего дня .. Пожалуйста, помогите мне, Риз .. - person Undefined; 24.11.2020