theZappr
theZappr

Reputation: 1

What is the right way to use FormSpy in React Final Form as an alternative to Redux-Form isDirty?

I have a large (3+ year old) React Redux project which uses a lot of Redux-Form. As that is now discontinued I am doing new features in react-final-form. It has been pretty easy to move to final-form but I was using some advanced features of redux-form that have no equivalent in final-form. I need a component that is outside of the form to know when the form is dirty, submitting etc.

Final-form suggests FormSpy for this, which works, but I have come up against React's Warning:

Cannot update during an existing state transition (such as within render)

So, really, this is as much a React as a Final-Form question. I have put an example app below illustrating the problem. In summary a parent Component <Parent/> renders a bunch of child components. One of those is a final-form instance <TheForm/>, one of the other child components, <ShowFormState/>, renders the form's state (eg whether the form is dirty). Importantly ShowFormState is a sibling of the form, not within it. In Redux-Form this was simple: ShowFormState could just grab the form's state from redux but final-form has no equivalent so the Parent uses FormSpy's onChange() and stores the state in it's own state and passes that to its child ShowFormState as props.

React no likey and I get the above error (which after 3 years of working in React I have not seen before!). The warning is totally understandable, that is not the question. I am just a bit stuck on how to work around it, with the given structure.

The sample App is below, I hope it is clear enough :). Anyone who has migrated from redux-form to final-form bumped into a similar problem?

import React , {Component} from 'react'
import {Form, FormSpy, Field } from 'react-final-form' 

class Parent extends Component {

  constructor(props){
    super(props)
    this.state = {}
  }

  onSubmit = () => { console.log("Submitted!") }

  onChange = ({dirty}) => {
    // triggers rerender DURING render so cant do this
    this.setState({ dirty })
  }

  render = () => (
    <>
      <TheForm
        onChange={this.onChange}
        onSubmit={this.onSubmit}
        />
      {/*other components ...*/}
      <ShowFormState dirty={this.state.dirty}/>
    </>
  )
}

const ShowFormState = ({dirty}) => dirty ? <p>dirty!</p> : null

const TheForm = ({onChange, onSubmit}) => (
  <Form
    onSubmit={onSubmit}
    initialValues={{ name:'' }}>
    {({ handleSubmit }) => (
      <form className="wrap" onSubmit={handleSubmit}>
        <label>
          Name:
          <Field name="name" component="input"/>
        </label>
        {<FormSpy onChange={onChange}/>}
      </form>
    )}
  </Form>
)

function App() {
  return <Parent /> 
}

export default App;

Thanks in advance for any help :)

Upvotes: 0

Views: 3941

Answers (0)

Related Questions