Управление формой с большим количеством полей - PullRequest
0 голосов
/ 07 апреля 2019

Я пишу форму в ReasonReact.Я использовал reducerComponent и запись как состояние.Допустим, у меня есть что-то вроде этого:

type state = {
  field1: string,
  field2: int,
};

type action =
  | SetField1(string)
  | SetField2(int);

let component = ReasonReact.reducerComponent("SomeComponent");

let make = ( _children) => {
  ...component,
  initialState: () => {field1: "", field2: 0},
  reducer: (action, state) => switch(action) {
    | SetField1(value) => ReasonReact.Update({...state, field1: value}) 
    | SetField2(value) => ReasonReact.Update({...state, field2: value})
  },
  render: ({state, send}) => 
    <div>
      <input value={state.field1} onChange={e => send(SetField1(getValue(e)))} />
      <input value={state.field2 |> string_of_int} onChange={e => send(SetField2(e |> getValue |> int_of_string))} />
    </div>,
}

В этом примере есть только 2 поля, но как справиться, если есть, например, 30 полей?Значит ли это, что мне нужно создать 30 различных действий и обработать это 30 раз в редукторе?Это много несущественного кода.Есть ли способ изменить запись более динамически, или, может быть, мне следует использовать другую структуру для состояния (object, Js.t)?

Чтобы уточнить, я использую этот вид форм в двух случаях:

  1. Чтобы преобразовать состояние в Js.Json.t (используя bs-json) и отправить на сервер (используя bs-fetch)
  2. Чтобы отправить его на сервер, используя reason-apollo (graphql) в качествемутация.

1 Ответ

0 голосов
/ 07 апреля 2019

Это будет во многом зависеть от ваших конкретных потребностей.То, что вы собираетесь делать с данными формы, сообщит, какую форму вы хотите иметь, а что еще нужно сделать в этом компоненте, будет информировать внутреннюю логику компонента.Но вот как минимум несколько идей:

Один из подходов - объединить все варианты SetField в один, который принимает функцию обновления состояния.Таким образом, вы можете указать, какое поле обновлять только в функции рендеринга:

  type action =
    | SetField(state => state);

  let make = _children => {
    ...component,
    initialState: () => {field1: "", field2: 0},
    reducer: (action, state) =>
      switch (action) {
      | SetField(updater) => ReasonReact.Update(updater(state))
      },
    render: ({state, send}) =>
      <div>
        <input
          value={state.field1}
          onChange={
            e => {
              let value = getValue(e);
              send(SetField(state => {...state, field1: value}));
            }
          }
        />
        <input
          value={string_of_int(state.field2)}
          onChange={
            e => {
              let value = e |> getValue |> int_of_string;
              send(SetField(state => {...state, field2: value}));
            }
          }
        />
      </div>,
  };

Но поскольку события React не являются неизменяемыми и фактически используются самим React, это немного подвержено ошибкам.И обходной путь добавляет многословие.Таким образом, если вы не сможете вычленить это многословие, такой подход может не иметь особого смысла.

Если все, что вы делаете в этом компоненте, это обновление состояния, вы можете полностью удалить тип action.Это устраняет некоторые из многословия выше, но в остальном имеет те же проблемы и также гораздо менее гибко.

  let make = _children => {
    ...component,
    initialState: () => {field1: "", field2: 0},
    reducer: (updater, state) =>
        ReasonReact.Update(updater(state)),
    render: ({state, send}) =>
      <div>
        <input
          value={state.field1}
          onChange={
            e => {
              let value = getValue(e);
              send(state => {...state, field1: value});
            }
          }
        />
        <input
          value={string_of_int(state.field2)}
          onChange={
            e => {
              let value = e |> getValue |> int_of_string;
              send(state => {...state, field2: value});
            }
          }
        />
      </div>,
  };
...