How to set/change Field value from external user action 🏁 React Final Form
Asked Answered
R

4

43

There are many situations in which we might want to set the value of a specific field within a form to help out the user.

For example, an online fruit store may ask users how many apples they want to buy. To help users out, we could have 3 buttons such as

  • "minimum" - sets field value to the minimum quantity the store can feasibly sell
  • "maximum" - ditto, but for max
  • "what I bought last time" - sets field value to the quantity of apples the user bought the last time

After going over what I thought were the two most relevant examples (Loading and Initializing Values and Calculated Fields), I still can't figure this one out without getting too hacky.

How can we set a field's value from a user's action (such as clicking a button)?

Ritaritardando answered 21/2, 2018 at 8:19 Comment(0)
R
61

This can be accomplished by providing a mutators object to the Form component containing the necessary mutators, which will be made available to your component:

<Form
  mutators={{
    setMin: (args, state, utils) => {
      utils.changeValue(state, 'apples', () => 1)
    },
    setMax: (args, state, utils) => {
      utils.changeValue(state, 'apples', () => 100)
    },
    setLast: (args, state, utils) => {
      utils.changeValue(state, 'apples', () => 6)
    },
  }}
  
  render={({ form, ...rest }) => (
    <>
      <button onClick={form.mutators.setMin}>minimum apples</button>
      <button onClick={form.mutators.setMax}>maximum apples</button>
      <button onClick={form.mutators.setLast}>what I bought last time</button>
    </>
  )}
/>
  

As suggested by @davnicwil below, if you need to interact with the form from outside the React app (eg, you're using micro front-ends, you're migrating the app, or have some third party lib that lives outside React) you can save the form object (or any methods within) to the global scope using the render method, and call them as needed:

render={({ form, ...rest }) => {
  window.form = form;

  // ... rest of code
)}

// later anywhere else in app
window.form.mutators.setMax

Erik's post

Ritaritardando answered 21/2, 2018 at 11:50 Comment(5)
where does the function changeValue() come from, is it inbuilt into final-form ?Quick
If you take a look at the snippet above, you'll notice that it's within the utils object provided by the library to the callback we createRitaritardando
It doesn't work for me. It says: Cannot read property 'setMin' of undefined Well, probably this info is outdated. Shouldn't we use FormSpy?Forwards
@Forwards using react-final-form 6.3.0, the mutators render prop is undefined. Use the form render prop instead; form.mutators.setMin.Lorileelorilyn
Any idea how can I make the changed field become "dirty"?Olen
B
27

The accepted answer is great and led me to my solution, but in case anyone comes here like I did looking for a way to modify field values from outside of the React application, you can do it by making a completely generic mutator that sets any field to any value, and getting a global reference to the bound mutator in the form like so

<Form
  mutators={{
    // expect (field, value) args from the mutator
    setValue: ([field, value], state, { changeValue }) => {
      changeValue(state, field, () => value)
    }
  }}
  render={({ form, ...rest }) => {
    // put the reference on a window variable of your choice here
    if (!window.setFormValue) window.setFormValue = form.mutators.setValue

    return (
      <>
        <Field name="field1">...</Field>
        <Field name="field2">...</Field>
        ...
      </>
    )
  }}
/>

// somewhere else, outside of the React application

window.setFormValue('field1', 'value1')
window.setFormValue('field2', 'value2')

The above is valid for v5.0.0 and up. In earlier versions the API was slightly different. Instead of using form.mutators, use the top level mutators prop for the render function i.e. render={({ mutators, ... }

Disclaimer: This pattern shouldn't be used unless absolutely necessary, for very particular usecases. For example, I'm using it for automating filling of fields from code which must be external to my React app. You shouldn't do this for actual application code. But if you do need to do it, it works great!

Basilicata answered 27/4, 2019 at 14:45 Comment(3)
What would you recommend if one would like to access a mutator function outside of the <Form> component? Imagine you have a Sidebar Component, which is permanently in your DOM, but not part of your Form Component.Lorrimor
Not the I'd ever want to use this, but thumbs up for the ingenuity!Nature
i think it's not good and perfect solution, but the only one, that i foundMohun
U
5

this worked for me. didn't have to preset mutators but the MinButton has to be a descendant component of the Form.

const MinButton = () => {
    const { change } = useForm();
    return <Button onClick={() => change('num_apples', 1)}/>;
};
Uriia answered 7/10, 2022 at 13:3 Comment(2)
This solution is much simpler than the main 2 answers. +1Sean
@AntoineOL thank you for your upvote. tell your friends :-PUriia
A
2

In case you want to change the value from within the form, define a mutator in the Form first.

<Form
      mutators={{ 
        setValue: ([field, value], state, { changeValue }) => {
          changeValue(state, field, () => value)
        }
      }}
/>

Make sure to pass that mutator name in the render function.

 render={({
        handleSubmit,
        form: { mutators: { setValue } } // pass custom mutator here
      }) 

Now, you can change the value of a field using the click of a button or some other event.

<Button onClick={() => setValue('fieldName','some value') }>Change Value</Button>

Thanks to @davnicwil for help.

Amphithecium answered 22/9, 2022 at 21:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.