Formik выполняет повторный рендеринг при изменении состояния реакции

avatar
Rishabh Sehgal
9 августа 2021 в 03:43
1449
1
0

Я использую пользовательский компонент в поле formik, и мне нужно вызвать функцию при изменении текста внутри функции (чтобы получить все соответствующие подсказки, связанные с вводом)

 <Field
            id="assignees"
            name="assignees"
            component={({
              field, // { name, value, onChange, onBlur }
              form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
              ...props
            }) => {
              return (
                    <input
                      key="assignees"
                      className="input-fields"
                      placeholder="Type names of assignees to start getting suggestions"
                      onChange={(e) => {
                        form.setFieldValue(field.name, e.target.value);
                        getMatching(field.value);
                      }}
                      {...props}
                      {...form}
                      //   {...field}
                    />
    );
}}
/>

Функция сопоставления, которая вызывается в верхнем коде, показана ниже. Он обновляет состояние, и это состояние затем отображается внутри диапазона в поле formik.

const getMatching = (keyword) => {
    for (const item of users) {
      if (keyword !== "" && item.name.startsWith(keyword)) {
        setMatching(item.name);
        return;
      } else {
        setMatching(undefined);
      }
    }
  };

Ожидаемое поведение
Ввод Formik не теряет фокус и сохраняет все введенные тексты, которые я добавил при изменении состояния или повторном рендеринге

Фактическое поведение
Компонент ввода теряет фокус, и поле сбрасывается всякий раз, когда состояние устанавливается функцией getMatching

РЕДАКТИРОВАТЬ КОД ПЕЧАТИ ССЫЛКА
https://codesandbox.io/s/wizardly-merkle-w191l<3><4244

Источник
Shyam
9 августа 2021 в 04:29
1

Вы можете добавить свой полный код в песочницу?

Rishabh Sehgal
9 августа 2021 в 05:36
0

конечно позвольте мне добавить

Bhuwan Adhikari
9 августа 2021 в 08:49
0

использовать рендеринг вместо компонента в элементе Field

Ответы (1)

avatar
Ro Milton
9 августа 2021 в 08:41
1

Несколько вещей:

  • нет необходимости хранить matching в состоянии, его можно получить из значения текстового поля уполномоченного и списка пользователей
  • текстовое поле правопреемника (я называю его searchAssignee) и список выбранных правопреемников (assignees) по существу являются двумя разными входными данными, поэтому они должны быть отдельными входными данными Formik
  • разделение двух указанных выше входных данных устраняет проблему с фокусом

Вся идея formik заключается в том, что он устраняет необходимость хранить входные значения в состоянии. Я удалил ненужное состояние и разделил входные данные выше.

Рабочий пример

const getMatching = (users, keyword) => {
  if (!keyword) return null;
  const match = users.find(({ name }) => name.startsWith(keyword));
  if (!match) return null;
  return match.name;
};

const FormContainer = ({ users }) => {
  return (
    <Formik
      initialValues={{
        summary: "",
        description: "",
        searchAssignee: "",
        assignees: []
      }}
      onSubmit={async (values) => {
        console.log(values);
      }}
    >
      <Form>
        <Row>
          <Col>
            <div className="textcolor">Summary</div>
            <Field
              id="summary"
              name="summary"
              placeholder="Add a short summary for your task"
              component={({
                field, // { name, value, onChange, onBlur }
                form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
                ...props
              }) => {
                return (
                  <input
                    className="input-fields"
                    {...props}
                    // {...form}
                    {...field}
                  />
                );
              }}
            />
          </Col>
        </Row>
        <Row>
          <Col>
            <div className="textcolor">Description</div>
            <Field
              id="description"
              name="description"
              placeholder="Description"
              component={({
                field, // { name, value, onChange, onBlur }
                form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
                ...props
              }) => {
                return (
                  <ReactQuill
                    theme="snow"
                    value={field.value}
                    onChange={field.onChange(field.name)}
                    style={{ minHeight: "5rem" }}
                    {...field}
                    {...props}
                  />
                );
              }}
            />
          </Col>
        </Row>
        <Row>
          <Col>
            <div className="textcolor">Assignees</div>
            <Col className="assignee-wrapper d-flex">
              <div className="d-flex">
                <Field
                  id="assignees"
                  name="assignees"
                  component={({
                    field, // { name, value, onChange, onBlur }
                    form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
                    ...props
                  }) => (
                    <>
                      {field.value.map((item) => (
                        <div
                          className="assignee-tag d-flex"
                          onClick={() => {
                            const newAssignees = field.value.filter(
                              (val) => val !== item
                            );
                            form.setFieldValue(field.name, newAssignees);
                          }}
                        >
                          <div>{item}</div>
                          <GrFormClose />
                        </div>
                      ))}
                    </>
                  )}
                />
              </div>
              <div className="d-flex col">
                <Field
                  id="searchAssignee"
                  name="searchAssignee"
                  placeholder="Type names of assignees to start getting suggestions"
                  component={({
                    field, // { name, value, onChange, onBlur }
                    form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
                    ...props
                  }) => {
                    const suggestion = getMatching(users, field.value);
                    return (
                      <>
                        <input
                          className="input-fields"
                          {...props}
                          {...form}
                          {...field}
                        />
                        {suggestion && (
                          <span
                            className="text-nowrap"
                            onClick={() => {
                              if (!form.values.assignees.includes(suggestion)) {
                                form.setFieldValue("assignees", [
                                  ...form.values.assignees,
                                  suggestion
                                ]);
                              }
                            }}
                          >
                            {suggestion}
                          </span>
                        )}
                      </>
                    );
                  }}
                />
              </div>
            </Col>
          </Col>
        </Row>
        <div className="mt-2 float-end">
          <button className="btn btn-dark btn-sm" type="submit">
            Add
          </button>
          <button className="btn btn-light btn-sm ms-2" type="reset">
            Cancel
          </button>
        </div>
      </Form>
    </Formik>
  );
};

const App = ({ isModalVisible, setModalVisible }) => {
  const [users, setUsers] = useState([
    { name: "Hello World" },
    { name: "Foo Bar" }
  ]);

  return (
    <Modal show={true}>
      <Modal.Body>
        <Row>
          <div>
            <GrFormClose
              className="float-end"
              onClick={() => setModalVisible(false)}
              style={{ cursor: "pointer" }}
            />
          </div>
          <div>
            <FormContainer
              users={users}
            />
          </div>
        </Row>
      </Modal.Body>
    </Modal>
  );
};