How to use semantic-ui-react Dropdown component as select in a (Availity) Form?

Viewed 430

I have recently started learning React, and it's been very interesting. But after struggling for a few days on the following, I thought I'd see if someone wants to help me out and think along what I'm missing.

Application info
I am creating a React application (in front of a Java Spring Boot backend). I am using jhipster as code generator. Amongst others, it generates CRUD pages for entities. Let's say I have the two entities 'Character' and 'Item'. The Character entity has various fields that will store references to Items. There is a large number of Items. Form validation is done through availity-reactstrap-validation, and the original code was structured as a with various or components in between.

Intent
On the Character create/edit screen, I want to use a dropdown component that has server-side search on the Item collection, for each of the Item-related fields. Upon submission of the form, the values of the dropdowns should be saved in the Character instance.

Approaches taken - and problems encountered
I had found the very nice Dropdown component that supports server-side search, in semantic-ui-react. I have linked up the onSearchChange with the search backend of Item, and it works great: the Dropdown options are changed as I type. However, upon submission of the form, the Dropdown value is not part of the 'values' list, and thus it is not saved.
I think this is due to the fact that the Dropdown component is not an 'input', and definitely not an AvInput - and thus not recognised and available as 'value' in the saveEntity function.

I then tried to use AvInput with the tag={ItemDropdown} attribute. The 'tag' attribute allows to use a custom component for rendering the actual component. This seemed great on paper. But here the struggle with state/props started. In this setup, the AvItemDropdown is parent of a AvInput, which is the parent of a ItemDropdown of a Dropdown. Upon change of the ItemDropdown, the value is set properly in that state. However, the state of AvInput remains unchanged. And I think that state is used in the form submission. I have tried all sorts of 'hacky' things, based on blogs, SO posts, documentation - but I guess I just lack the understanding of how React works, or at least, with these specific packages.

Screenshot from Chrome DevTools React Components tree

Question How can I use the Dropdown component that I created, but somehow update the state of AvInput to make it work in this context of the form?

Code snippets

Form:

<AvForm model={characterEntity} onSubmit={saveEntity}>
 <AvGroup>
   <AvField id="character-item2" type="text" name="item2" /> // This works, as simple text input. Changed values here are saved properly. I want to replace this with the Dropdown component
 </AvGroup>
 <AvGroup>
   <ItemDropdown /> // This was the first attempt. Working dropdown, but not saving
   <AvItemDropdown name="item1" id="character-item1" /> // This is my second attempt, also not working
 </AvGroup>
</AvForm>

SaveEntity:

const saveEntity = (event, errors, values) => {
  console.log(values) //this
  const entity = {
    ...characterEntity,
    ...values,
  };
  props.updateEntity(entity);    
}

AvItemDropdown (in separate .tsx file):

class ItemDropdown extends Component {
  state = {
    isFetching: false,
    value: "",
    options: []
  }

  handleChange = (e, { value }) => {
    this.setState({ value })
  }

  onSearchChange = (e, value) => {
    axios.get(`/api/_search/items?query=${value.searchQuery}`)
    .then((response) => {
      const opts = []
      _.forOwn(response.data, function(v, k) {
        opts.push({key: v.id, text: v.name, value: v.name})
      })
      this.setState({options: opts})
    })
  }

  render() {
    const { isFetching, value, options } = this.state

    return (
          <Dropdown
            fluid
            selection
            search
            options={options}
            value={value}
            text={value}
            placeholder='Select item'
            onChange={this.handleChange}
            onSearchChange={this.onSearchChange}
            disabled={isFetching}
            loading={isFetching}
          />
    )
  }
}

export default function AvItemDropdown (props) {
  return <AvInput {...props} tag={ItemDropdown}  />;
}

PS. this is my first question (yay community!) so please let me know if more/other info would be helpful.

0 Answers
Related