管理具有很多字段的表单

时间:2019-04-07 09:42:25

标签: record reason bucklescript reason-react

我正在使用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个不同的动作并在reducer中处理30次?这是很多无关紧要的代码。有什么方法可以更动态地修改记录,或者我应该为状态(对象,Js.t)使用另一种结构?

为澄清起见,我在两种情况下使用了这种形式:

  1. 将状态转换为Js.Json.t(使用bs-json)并发送到服务器(使用bs-fetch)
  2. 使用原因阿波罗(graphql)作为变异将其发送到服务器。

1 个答案:

答案 0 :(得分:0)

这将在很大程度上取决于您的特定需求。您将要对表单数据进行的操作将告知您希望它具有什么形状,而在此组件中需要执行的其他操作将通知该组件的内部逻辑。但是至少这里有一些想法:

一种方法是将所有SetField变体组合为仅带有状态更新功能的一种。这样,您可以仅在render函数中指定要更新的字段:

  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>,
  };