0

I'm using material-ui-chip-input wrapped with Field react-final-form. enter image description here

I wanted to limit "CHIPS" to 5 only.

Chips are compact elements that represent an input, attribute, or action.

material ui docs

As you can see I'm using 2 states here

  1. react useStates
  2. react-final-form internal states

This is redundant because react-final-form handled states internally but I can't make to work I'm just showing what I have done so far.

Basically there are two problems with my implementation.

  1. It doesn't limit my chips.
  2. My react-final-form field values - not updating when clicking DeleteChip
import ChipInput from 'material-ui-chip-input'
import { Field } from 'react-final-form'

const [state, setState] = useState([])

const AddChip = useCallback(
    (chip) => {
        if (state.length + 1 <= 5) {
        setState((prev) => ([...prev.tags, chip]))
        }
    },
    [setState, state.length]
)

const DeleteChip = useCallback(
    (chip) => {
        setState((prev) => (...prev.state.filter((p) => p !== chip)]
        }))
    },
    [setState]
)

  return (
    <Field name="states" validate={isRequired} >
          {({ input: { value, onChange, ...rest }, meta }) => {
     <ChipInput
        defaultValue={Array.isArray(value) ? value : []} // check value first because material-ui-chip-input require an array, by default react-final-form value is empty string
        onChange={(event) => {  // uncontrolled 
            AddChip(event)
            onChange(event)
            // I tried below code but same result not working
            // if (state.length + 1 <= 5) {
            //  onChange(event)
            // }
        }}
        onDelete={DeleteChip} 
       />
      }}
    </Field>
  )

material-ui-chip-input

react-final-form

see codesandbox demo

devjson
  • 113
  • 2
  • 16

1 Answers1

2

This is my take: https://codesandbox.io/s/proud-water-xp2y1?file=/src/App.js

Key points:

  • Don't duplicate the state, let react final form handle the state for you
  • Pass an empty array as the initial state to the FORM, don't pass defaultValues to the Field.
  • according to the material-ui-chip-input package you need to use onAdd if used in controlled mode, which we do, since we let react final form handle the state.
  • Add the value prop to the Chipinput.
  • For cosmetic reasons: don't actually use the render-prop inside <Form /> - use a functional child component instead.

Code:

import ChipInput from "material-ui-chip-input";
import { Form, Field } from "react-final-form";

export default function App() {
  return (
    <Form
      initialValues={{
        states: []
      }}
      onSubmit={() => console.log("submitted")}
    >
      {({ values, onSubmit }) => (
        <form onSubmit={onSubmit}>
          <Field name="states">
            {({ input: { value, onChange } }) => (
              <ChipInput
                value={value}
                alwaysShowPlaceholder={true}
                placeholder="type states here"
                onAdd={(newVal) => {
                  if (value.length >= 5) return;
                  const newArr = [...value, newVal];
                  onChange(newArr);
                }}
                onDelete={(deletedVal) => {
                  const newArr = value.filter((state) => state !== deletedVal);
                  onChange(newArr);
                }}
              />
            )}
          </Field>
          <p>react useStates</p>

          <p>react-final-form values</p>
          <pre
            style={{
              backgroundColor: "rgba(0,0,0,0.1)",
              padding: "20px"
            }}
          >
            {JSON.stringify(values, 0, 2)}
          </pre>
        </form>
      )}
    </Form>
  );
}
Dom
  • 734
  • 3
  • 8
  • Yeah, that's correct no need to use useState because react-final-form handle state internally. Your demo is great, problem with mine is I don't want to put intialValue on the
    . I added defaultValue on got infinite loop.instead.
    – devjson Feb 05 '21 at 23:29
  • Here's my work around instead. Remove defaultValue from props then in my value is value={isEmpty(value) ? [] : value} – devjson Feb 05 '21 at 23:40
  • why do you not want that on the Form? That's the intended use for this – Dom Feb 06 '21 at 14:32
  • I'm working on wizard style form like in the demo of react-final-form but in materia-ul stepper implementation. I have multiple screens each screen react-final-form saved the values from each fileds but when moving next page the values are gone. So yeah, that's why I not initializing default values in the
    itself.
    – devjson Feb 07 '21 at 00:48
  • I think this post is helpful regarding [initialValues is resetting issue](https://stackoverflow.com/questions/54635276/react-final-form-set-initialvalues-from-props-form-state-resets-on-props-chang) – devjson Feb 10 '21 at 02:23
  • can help me with this https://stackoverflow.com/questions/66168054/react-final-form-multiple-wizard-screen-next-button-and-form-validity-is-alwa – devjson Feb 12 '21 at 07:50