Tagify: Using with Formik

Created on 25 Sep 2020  路  5Comments  路  Source: yairEO/tagify

Hi there

I'm having problems to set up tagify inside a Formik component on a Next JS app.

Code:

import { Formik, Form } from 'formik';
import Tags from "@yaireo/tagify/dist/react.tagify";

const App = () => {
  const validate = val => {
    const errors = {};
    Object.keys(val).map(field => {
      if (val[field] === '') {
        errors[field] = 'Required';
      }
    })
    return errors;
  }
  return (
    <Formik
      initialValues={{
        keywords: '[{"value":"a"},{"value":"b"},{"value":"c"}]'
      }}
      validate={e => validate(e)}
    >
      {({ values, setFieldValue }) => (
        <Form style={{ width: "50%", margin: "20%" }}>
          <Tags
            value={values['keywords']}
            onChange={e => {
              setFieldValue('keywords', e.target.value)
            }}
          />
          Values: {values['keywords']}
        </Form>
      )}
    </Formik>
  );
}

export default App;

Result:
Issue

As you can see, only the last text entered remains after typing Enter.

I made a repository in https://github.com/erniomaldo/tagify_formik with the problem, you can install it and see for yourself.

What am I doing wrong?

Thank you very much in advance

Help wanted

All 5 comments

Apparently some small delay was needed. I've also improved your code:

import { Formik, Form } from 'formik';
import Tags from "@yaireo/tagify/dist/react.tagify";

// if a function is pure function and is not connected to the component's state, then its
// best to be placed outside of the component, so it won't be re-create on every component render
const validate = val => {
  const errors = {}

  for( let field in val )
    if (val[field] === '')
      errors[field] = 'Required'

  return errors
}

// same as above
const onChange = setFieldValue => e => {
  e.persist()
  setTimeout(setFieldValue, 0, 'keywords', e.target.value)
}

const App = () => (
  <Formik
    initialValues={{
      keywords: '[{"value":"a"},{"value":"b"},{"value":"c"}]'
    }}
    validate={validate}
  >
    {({ values, setFieldValue }) => (
      <Form style={{ width: "50%", margin: "20%" }}>
        <Tags
          value={values['keywords']}
          onChange={onChange(setFieldValue)}
        />
        Values: {values['keywords']}
      </Form>
    )}
  </Formik>
)

export default App

Hi again

I have some problems after update to NextJS 10, solved this way:

<Tags
  value={values['keywords']}
  onChange={e => {
    setFieldValue('keywords', e.target.value)
  }}
/>

Because this seems to be the only Google hit on this problem, I thought I'd share my solution:

import { FieldArray } from 'formik'
import Tags from "@yaireo/tagify/dist/react.tagify";

const FormikTags = ({...props}) => {
    return (
        <FieldArray {...props}>
            {({ push, remove, form }) => (
                <div>
                    <label htmlFor={props.name}>{props.label}</label>
                    <Tags
                        value={form.initialValues[props.name]}
                        onAdd={e => push(e.detail.data.value)}
                        onRemove={e => remove(e.detail.index)}
                    />
                </div>
            )}
        </FieldArray>
    )
}

export default FormikTags;

Usage:

import {Formik, Form} from 'formik'

const ExampleForm = () => {
    const handleSubmit = (values) => {
        console.log("Submitting with values", values);
    }

    return (
        <Formik initialValues={{tags: ["these"]}} onSubmit={handleSubmit}>
            <Form>
                <FormikTags 
                    name="tags"
                    id="tags"
                    label="Tags"
                    tagProps={{
                        settings: {
                            enforceWhitelist: true
                        },
                        whitelist: ["only", "these", "allowed"],
                        className: "customTags"
                    }}
                />
                <button type="submit">Submit</button>
            </Form>
        </Formik>
    );
}

export default ExampleForm;

Note that there are a bunch of issues to be sorted out, for example if enforceWhitelist is true, but the initialValues supplied to the component contains an item not in the whitelist, that kind of breaks it. Also this doesn't work for mixed more or select mode. There are probably more problems, but hopefully this is a useful template for whoever sees this to work from!

"if enforceWhitelist is true, but the initialValues supplied to the component contains an item not in the whitelist, that kind of breaks it"

It is up to you, as the implementer to pre-filter the initialValues. This is not a bug but on purpose, to allow manually by-passing the enforceWhitelist with initial values.

I assumed developers won't save to the database unwanted values in the first-place

Oh yeah I didn't mean to imply that was a problem with tagify, just that my example implementation was very barebones! Tagify is bloody amazing 馃憤

Was this page helpful?
0 / 5 - 0 ratings