Repository: h2oai/wave
File: ui/src/combobox.tsx (TypeScript)

Code before:
      required={m.required}
      selectedKey={selected}
      allowFreeform
      disabled={m.disabled}
      autoComplete="on"
      errorMessage={m.error}
      onChange={onChange}
    />
  )
}

const ComboboxMultiSelect = ({ model: m }: { model: Omit<Combobox, 'value'> }) => {
  const
    [options, setOptions] = useOptions(m.choices),
    [selected, setSelected] = React.useState<S[]>(m.values ?? []),
    selectOpt = (option: IComboBoxOption) => {
      setSelected(keys => {
        const result = option.selected ? [...keys, String(option.key)] : keys.filter(key => key !== option.key)
        wave.args[m.name] = result
        return result
      })
    },
    onChange = (_e: React.FormEvent<IComboBox>, option?: IComboBoxOption, _index?: U, value?: S) => {
      if (!option && value) {
        const opt: IComboBoxOption = { key: value, text: value }
        setOptions((prevOptions = []) => [...prevOptions, opt])
        selectOpt({ ...opt, selected: true })
      }
      if (option) {
        selectOpt(option)
      }
      if (m.trigger) wave.push()
    }

  React.useEffect(() => {
    wave.args[m.name] = m.values?.length ? m.values : null
    setSelected(m.values || [])
    if (m.values?.length) {
      setOptions((prevOptions = []) =>
        [
          ...prevOptions,
          ...m.values!.filter(v => !prevOptions.some(o => o.key === v)).map(v => ({ key: v, text: v }))
        ]
      )
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [m.values])

  return (
    <Fluent.ComboBox
      data-test={m.name}

Review comment (suggestion):
Can using the `flushSync` be avoided? This should be enough:
```suggestion
      const result = option.selected ? [...selected, String(option.key)] : selected.filter(key => key !== option.key)
      setSelected(result)
      wave.args[m.name] = result
      if (m.trigger) wave.push()
```

Code after:
      options={options}
      required={m.required}
      selectedKey={selected}
      allowFreeform
      disabled={m.disabled}
      autoComplete="on"
      errorMessage={m.error}
      onChange={onChange}
    />
  )
}

const ComboboxMultiSelect = ({ model: m }: { model: Omit<Combobox, 'value'> }) => {
  const
    [options, setOptions] = useOptions(m.choices),
    [selected, setSelected] = React.useState<S[]>(m.values ?? []),
    selectOpt = (option: IComboBoxOption) => {
      flushSync(() => {
        setSelected(keys => {
          const result = option.selected ? [...keys, String(option.key)] : keys.filter(key => key !== option.key)
          wave.args[m.name] = result
          return result
        })
      })

      if (m.trigger) wave.push()
    },
    onChange = (_e: React.FormEvent<IComboBox>, option?: IComboBoxOption, _index?: U, value?: S) => {
      if (option) selectOpt(option)
      if (!option && value) {
        const opt: IComboBoxOption = { key: value, text: value }
        setOptions((prevOptions = []) => [...prevOptions, opt])
        selectOpt({ ...opt, selected: true })
      }
    }

  React.useEffect(() => {
    wave.args[m.name] = m.values?.length ? m.values : null
    setSelected(m.values || [])
    if (m.values?.length) {
      setOptions((prevOptions = []) =>
        [
          ...prevOptions,
          ...m.values!.filter(v => !prevOptions.some(o => o.key === v)).map(v => ({ key: v, text: v }))
        ]
      )
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [m.values])

  return (