从状态初始化为带有react-final形式的react-select的条件下拉列表

时间:2019-11-08 21:43:01

标签: reactjs react-select react-final-form react-final-form-arrays

我使用react-selectreact-final-form进行条件下拉菜单,其中<PickOptions/>组件根据第一次选择的值提供第二次选择的选项(感谢这样做) answer)。

以下是组件:

/** 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)时,它将清除第二个选择。我还通过传递initialValue将第二选择集设置为数组中的第一个选项。由于需要从状态初始化值,因此我得到了以下代码:

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),则将传递值,但是当第一个选择的值更改时(即使选项已更新),第二个选择的输入值也不会更新。这是指向我的codesandbox的链接。

我该如何解决?

2 个答案:

答案 0 :(得分:1)

我能够通过将fields.map()节映射的所有内容都包装并包装在其自己的组件中以确保它们各自具有独立的状态来使其工作。然后,我将changeB(undefined)函数放在useEffect钩子的返回调用中,以在用户为第一个选择选择其他选项之后清除次要选择,如下所示:

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

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

您可以在以下沙箱中查看其工作方式:React Final Form - Clear Secondary Selects

要更改辅助选择字段,您将需要为数组对应的选项类型传递一个额外的道具给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

我也将您的类组件更改为功能,因为它使我更容易查看和测试正在发生的事情,但是此方法也应与您的类组件一起使用。

答案 1 :(得分:0)

基于上一个答案,我在组件中添加了以下代码:

// 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,如果为true,则将bField的值设置为数组中的第一个选项。