从状态初始化的 react-final-form 中带有 react-select 的条件下拉列表

Conditional dropdowns with react-select in react-final-form initialized from the state

我将 react-selectreact-final-form 用于条件下拉菜单,其中第二个 select 的选项由 <PickOptions/> 组件根据首先 select(多亏了这个 SO )。

组件如下:

/** Changes options and clears field B when field A changes */
const PickOptions = ({ a, b, optionsMap, children }) => {
  const aField = useField(a, { subscription: { value: 1 } });
  const bField = useField(b, { subscription: {} });
  const aValue = aField.input.value.value;
  const changeB = bField.input.onChange;
  const [options, setOptions] = React.useState(optionsMap[aValue]);

  React.useEffect(() => {
    changeB(undefined); // clear B
    setOptions(optionsMap[aValue]);
  }, [aValue, changeB, optionsMap]);
  return children(options || []);
};

当第一个值改变changeB(undefined)时,它清除第二个select。我还通过传递 initialValue 将第二个 select 设置为数组中的第一个选项。因为我需要从状态中初始化值,所以我得到了以下代码:

initialValue={
  this.state.data.options[index] &&
  this.state.data.options[index].secondOption
    ? this.state.data.options[index]
        .secondOption
    : options.filter(
        option => option.type === "option"
      )[0]
}

但是没用。状态的初始值未传递到 <PickOptions/> 呈现的字段。如果我从组件中删除 changeB(undefined),值会被传递,但第二个 select 的输入值不会更新,当第一个 select 的值发生变化时(即使选项已更新)。这是我的 codesandbox.

的 link

我该如何解决?

我能够通过获取 fields.map() 部分映射的所有内容并将其包装在它自己的组件中以确保它们中的每一个都有单独的状态来实现它。然后我只是将 changeB(undefined) 函数放在 useEffect 挂钩的 return 调用中,以在用户 selects 之后清除辅助 selects 一个不同的选项首先 select 像这样:

React.useEffect(() => {
  setOptions(optionsMap[aValue]);

  return function cleanup() {
    changeB(undefined) // clear B
  };
}, [aValue, changeB, optionsMap]);

您可以在这个沙箱中看到它是如何工作的:React Final Form - Clear Secondary Selects

要更改辅助 select 字段,您需要向 PickOptions 传递一个额外的道具,用于数组对应的选项类型。我还订阅并跟踪以前的 bValue 以检查它是否存在于当前 bValueSet 数组中。如果它存在,我们就不管它,否则我们用它对应的 optionType 数组中的第一个值更新它。

  // subscibe to keep track of previous bValue
  const bFieldSubscription = useField(b, { subscription: { value: 1 } })
  const bValue = bFieldSubscription.input.value.value

  React.useEffect(() => {
    setOptions(optionsMap[aValue]);

    if (optionsMap[aValue]) {
      // set of bValues defined in array
      const bValueSet = optionsMap[aValue].filter(x => x.type === optionType);

      // if the previous bValue does not exist in the current bValueSet then changeB
      if (!bValueSet.some(x => x.value === bValue)) {
        changeB(bValueSet[0]); // change B
      }
    }

  }, [aValue, changeB, optionsMap]);

这是该方法的沙箱:React Final Form - Update Secondary Selects

我还将您的 class 组件更改为功能组件,因为这样我可以更轻松地查看和测试正在发生的事情,但此方法也适用于您的 class 组件。

根据之前的回答,我在我的组件中得到了以下代码:

// subscibe to keep track of aField has been changed
const aFieldSubscription = useField(a, { subscription: { dirty: 1 } });

React.useEffect(() => {
  setOptions(optionsMap[aValue]);

  if (optionsMap[aValue]) {
    // set of bValues defined in array
    const bValueSet = optionsMap[aValue].filter(x => x.type === optionType);

    if (aFieldSubscription.meta.dirty) {
      changeB(bValueSet[0]); // change B
    }
  }
}, [aValue, changeB, optionsMap]);

这样它会检查 aField 是否已被用户更改,如果是,它会将 bField 的值设置为数组中的第一个选项。