-1

我正在 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. 使用 reason-apollo (graphql) 作为突变将其发送到服务器。
4

1 回答 1

0

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

一种方法是将所有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>,
  };
于 2019-04-07T10:56:57.627 回答