HiLuLiT
HiLuLiT

Reputation: 553

Migration from react-redux to react-final-form

I updated my react-admin project to v3, some of the changes in this version update included migration from from react-redux to react-final-form and i'm trying to fix some bugs related to this change.

I'm probably not using react-final-form right- I used to use the .change() function which connected my changed input field values to the store.

I followed react-final-form docs, and tried changing it like this:

get the form:

const form = useForm()

then use it's .change method to update my UI :

form.change('isadmin', this.state.isAdmin)

but im getting this error:

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

here's is how my AdminButton component looks like:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import Switch from '@material-ui/core/Switch'
import { showNotification, fetchStart, fetchEnd, refreshView } from 'react-admin'
import { httpClient } from '../../dataProvider'
import { getFormValues } from 'redux-form'
import { useForm } from 'react-final-form'

const AdminButtonStyle = {
  zIndex: 2,
  display: 'inline-block',
  float: 'right',
  color: 'black'
}

class AdminButton extends Component {
  state = {
    isAdmin: this.props.formStates && this.props.formStates.isadmin
  }

  handleClick = async () => {
    const form = useForm()
    const { fetchStart, fetchEnd, record, showNotification } = this.props
    this.setState({
      isAdmin: !this.state.isAdmin
    })
    fetchStart()
    try {
      await httpClient(
        `api/users/${record.id}`,
        { method: 'PATCH', body: JSON.stringify({ isadmin: !this.state.isAdmin }) })
      form.change('isadmin', this.state.isAdmin)
      showNotification(`toggled user admin: ${this.state.isAdmin}`, 'success', { autoHideDuration: 10000 })
    } catch (err) {
      showNotification(`Error: ${err.data || err.message}`, 'warning', { autoHideDuration: 10000 })
    } finally {
      fetchEnd()
    }
  }

  render () {
    return <div style={AdminButtonStyle}>
      <span>Make Admin</span>
      <Switch
        checked={this.state.isAdmin}
        variant='raised'
        color='primary'
        size='large'
        onClick={this.handleClick}>
      </Switch>
    </div>
  }
}

function mapStateToProps (state) {
  return {
    formStates: getFormValues('record-form')(state)
  }
}

export default connect(mapStateToProps, {
  fetchStart,
  fetchEnd,
  showNotification,
  refreshView
})(AdminButton)

So can anyone help me understand what i'm doing wrong? Many thanks!

Upvotes: 0

Views: 1043

Answers (2)

frtelg
frtelg

Reputation: 81

You are using a React hook inside of a Class component. That's not allowed. Try to convert your component to a functional component.

Upvotes: 0

Striped
Striped

Reputation: 2547

You can't use the useForm or useFormState outside a Form context. That said, try to wrap your component inside a Form like

import { SimpleForm } from 'react-admin';

<SimpleForm {...props}>
  ... // your component
</SimpleForm>

or use the react-final-form component directly

import { Form } from 'react-final-form';

<Form onSubmit={handleSubmit}>
  {({ handleSubmit }) => (
    <form onSubmit={handleSubmit}>
    ... // You component
    </form>
 )}
</Form>

Then in you component, you can use useForm hook like

import {useForm} from 'react-final-form';

const AdminButton = () => {
  const form = useForm();
    
  const handleClick = async () => {
    ...
    form.change('isadmin', isAdmin);
    ...
  }
  ...
}

Upvotes: 2

Related Questions