Tagify: Cannot get whitelist/dropdown to work in React

Created on 4 Aug 2020  路  4Comments  路  Source: yairEO/tagify

I'm trying to use tagify in a React app. I want it to build the whitelist as the user types. This is what I've got:

const HandleChange = event => {
  event.persist()
  console.log("CHANGED:", event.target.value)
}

const ProjectAdmin = () => {
  const tagifyRef = useRef()
  const [tagifySettings, setTagifySettings] = useState([])
  const [tagifyProps, setTagifyProps] = useState({})
  const tagifyCallbacks = {
    input: onInput,
  }
  const baseTagifySettings = {
    placeholder: "Type to search",
    dropdown: {
      enabled: 0,
      position: "text",
    },
  }
  const settings = {
    ...baseTagifySettings,
    ...tagifySettings,
    callbacks: tagifyCallbacks,
  }
  var controller

  function onInput(e) {
    var value = e.detail.value
    setTagifySettings(lastState => ({
      ...lastState,
      whitelist: [],
    }))
    if (value !== "") {
      // https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort
      controller && controller.abort()
      controller = new AbortController()
      // show loading animation and hide the suggestions dropdown
      setTagifyProps({ loading: true, showDropdown: false })

      fetch(`${apiOrigin}/users?query=${value}`, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${tokens.accessToken}`,
        },
        signal: controller.signal,
      })
        .then(result => result.json())
        .then(whitelist => {
          console.log(whitelist)
          let tempList = []
          whitelist.forEach(s => {
            tempList.push(s["username"])
          })
          console.log("Whitelist: ", tempList)
          setTagifySettings(lastState => ({
            ...lastState,
            whitelist: tempList,
          }))
          setTagifyProps({ loading: false, showDropdown: true })
        })
        .catch(error => {
          console.log(error)
          setTagifySettings({ loading: false })
        })
    }
  }

  return (
    <Layout pageTitle="Project Control">
      <Header contentPage={true} className="contentPage">
        <h1 className="display-4 font-weight-bold">Project Control</h1>
      </Header>
      <Content>
        <Row>
          <Col xs={{ size: 12 }}>
            <Tags
              tagifyRef={tagifyRef}
              settings={settings}
              value="a,b,c"
              {...tagifyProps}
              onChange={HandleChange}
            />
          </Col>
        </Row>
      </Content>
    </Layout>
  )
}

As far as I can tell, the whitelist is being built and set correctly as shown by this capture from the React dev tools:

{
  "tagifyRef": {
    "current": "{CSSVars: {鈥, DOM: {鈥, isFirefox: false, isIE: un鈥"
  },
  "settings": {
    "placeholder": "Type to search",
    "dropdown": "{enabled: 0, position: \"text\"}",
    "loading": false,
    "whitelist": "[\"pjc-test3\", \"pjc-test2\", \"pjc-test1\"]",
    "callbacks": "{input: 茠 onInput() {}}"
  },
  "value": "a,b,c",
  "loading": false,
  "showDropdown": true,
  "onChange": "茠 HandleChange() {}"
}

However, the dropdown never appears. If I enable enforcement of the whitelist and continue typing, say, _pjc-test3_ and press Return, it gets added but then removed, even though the value is in the whitelist.

It is probably something obvious but I'm struggling to see what I've got wrong.

Thanks.

Bug

All 4 comments

I will investigate asap, thanks for reporting. I suspect showDropdown prop was forgotten to be implemented in my large code-refactor last month, when I completely re-wrote the React wrapper file. will be fixed tomorrow.

sorry about that

Hi @yairEO

I've done some more testing. You will obviously know your code better than I do but I'm not entirely sure it is down to showDropdown.

I've replaced the code with the following:

  const tagifyRef = useRef()
  const [tagifySettings, setTagifySettings] = useState({
    whitelist: [
      { id: 100, value: "kenny", title: "Kenny McCormick" },
      { id: 101, value: "cartman", title: "Eric Cartman" },
      { id: 102, value: "kyle", title: "Kyle Broflovski" },
      { id: 103, value: "token", title: "Token Black" },
    ],
  })
  const [tagifyProps, setTagifyProps] = useState({})
  const tagifyCallbacks = {
    input: onInput,
  }
  const baseTagifySettings = {
    placeholder: "Type to search",
    enforceWhitelist: true,
    dropdown: {
      enabled: 0,
      position: "text",
    },
  }
  const settings = {
    ...baseTagifySettings,
    ...tagifySettings,
    callbacks: tagifyCallbacks,
  }

  function onInput(e) {
    var value = e.detail.value
    if (value !== "") {
      setTagifyProps({ loading: true }) // , showDropdown: false

      let tempList = [
        { id: 107, value: "randy", title: "Randy Marsh" },
        { id: 108, value: "Mr. Garrison", title: "POTUS" },
        { id: 109, value: "Mr. Mackey", title: "M'Kay" },
      ]
      setTagifySettings(lastState => ({
        ...lastState,
        whitelist: tempList,
      }))
      setTagifyProps({ loading: false }) // , showDropdown: true
    }
  }

When the page loads and I click in the tag component, the dropdown immediately displays the four South Park names and clicking on one adds it to the tag field.

If I type a single letter, no dropdown is displayed. If I then delete that letter, the dropdown is displayed but with the three remaining names!

So:

  1. The lines to replace the whitelist aren't working.
  2. Curiously, even if you type a letter that matches one of the three remaining names, the dropdown still isn't displayed.
  3. Adding or removing the showDropdown prop change doesn't make any difference.

I thought I'd share this simpler code in case it helps.

Thanks for looking at this so quickly - much appreciated.

I am working hard on this problem, it's a very interesting one.
it will be solved promptly.

Ok, just released a new version 3.17.0 which incorporates the fix in the React wrapper file, but it also requires a different thinking when modifying the whitelist after the component has been initialized.

Instead of this part:

setTagifySettings(lastState => ({
    ...lastState,
    whitelist: tempList,
}))
setTagifyProps({ loading: false }) // , showDropdown: true

Do:

setTagifyProps(lastProps => ({
    ...lastProps,
    whitelist: tempList,
    loading: false,
    showDropdown: value  // your user-input value which was last-typed
}))

It is impossible to modify the whitelist via the settings after the component was initialized, but it is possible via a dedicated whitelist React prop, therefore I use setTagifyProps and not setTagifySettings

Hope this helps!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gencer picture gencer  路  4Comments

biznickman picture biznickman  路  6Comments

danqing picture danqing  路  6Comments

raoul2000 picture raoul2000  路  6Comments

thetuningspoon picture thetuningspoon  路  4Comments