React-admin: [Alpha4][v3] Error: useForm must be used inside of a <Form> component

Created on 12 Sep 2019  路  4Comments  路  Source: marmelab/react-admin

What you were expecting:
I followed the upgrade guide for linking two components and started using const form = useForm() instead of dispatch. The change works in Chrome browser but when I open in Electron I get the error:

Error: useForm must be used inside of a <Form> component

I'm not sure how to proceed from here and it is strange that it works in chrome but not electron so any help would be greatly appreciated.

import React, { Component, Fragment } from 'react';
import LocationPicker from 'react-location-picker';
import PlacesAutocomplete, {
  geocodeByAddress,
  getLatLng
} from 'react-places-autocomplete';

/* Default position */
const defaultPosition = {
  lat: 31.218001175963728,
  lng: 121.44911770820613
};
const LocationPicker = ({ formData, classes, ...rest }) => {
    const form = useForm()
  return  (
    <Fragment>
      <TextInput source="Label_Longitude" {...rest} disabled />
      <TextInput source="Label_Latitude" {...rest} disabled />
      <br />
      <TextInput source="FullAddress" style={{ width: '60%' }} {...rest} />
      <LocationPickerComponent lpform={form} {...rest} />
    </Fragment>
  );
    }

const FactoryEdit = ({ classes, ...props }) => (
  <Edit title={<FactoryTitle />} {...props}>
 <TabbedForm>
  <FormDataConsumer>
          {formDataProps => <LocationPicker {...formDataProps} />}
  </FormDataConsumer>
</TabbedForm>
</Edit>
)

And here is the location picker custom components

import React, { Component, Fragment } from 'react';
import LocationPicker from 'react-location-picker';
import PlacesAutocomplete, {
  geocodeByAddress,
  getLatLng
} from 'react-places-autocomplete';

/* Default position */
const defaultPosition = {
  lat: 31.218001175963728,
  lng: 121.44911770820613
};
class LocationPickerComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      address: '',
      position: {
        lat: this.props.record.Label_Latitude
          ? this.props.record.Label_Latitude
          : defaultPosition.lat,
        lng: this.props.record.Label_Longitude
          ? this.props.record.Label_Longitude
          : defaultPosition.lng
      }
    };

    // Bind
    this.handleLocationChange = this.handleLocationChange.bind(this);
  }

  handleLocationChange({ position, address }) {
    // Set new location
    const form = this.props.lpform
    form.change('Label_Longitude', position.lng);
    form.change('Label_Latitude', position.lat);
    form.change('FullAddress', address);
  }

  handleChange = address => {
    this.setState({ address });
  };

  handleSelect = address => {
    geocodeByAddress(address)
      .then(results => getLatLng(results[0]))
      .then(latLng => {
        this.setState({ position: latLng });
        console.log('Success', latLng);
      })
      .catch(error => console.error('Error', error));
  };

  render() {
    return (
      <div>
        <div>
          <PlacesAutocomplete
            value={this.state.address}
            onChange={this.handleChange}
            onSelect={this.handleSelect}
          >
            {({
              getInputProps,
              suggestions,
              getSuggestionItemProps,
              loading
            }) => (
              <div>
                <input
                  {...getInputProps({
                    placeholder: 'Search Places ...',
                    className: 'location-search-input'
                  })}
                />
                <div className="autocomplete-dropdown-container">
                  {loading && <div>Loading...</div>}
                  {suggestions.map(suggestion => {
                    const className = suggestion.active
                      ? 'suggestion-item--active'
                      : 'suggestion-item';
                    // inline style for demonstration purpose
                    const style = suggestion.active
                      ? { backgroundColor: '#fafafa', cursor: 'pointer' }
                      : { backgroundColor: '#ffffff', cursor: 'pointer' };
                    return (
                      <div
                        {...getSuggestionItemProps(suggestion, {
                          className,
                          style
                        })}
                      >
                        <span>{suggestion.description}</span>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
          </PlacesAutocomplete>

          <LocationPicker
            containerElement={<div style={{ height: '100%' }} />}
            mapElement={<div style={{ height: '400px' }} />}
            defaultPosition={this.state.position}
            onChange={this.handleLocationChange}
          />
        </div>
      </div>
    );
  }
}
export default LocationPickerComponent;

Environment

  • React-admin version: alpha.4
  • Last version that did not exhibit the issue (if applicable): v2
  • React version: ^16.8
  • Browser: Electron
    "electron": "^6.0.8",
    "electron-builder": "^21.1.1",
    "electron-devtools-installer": "^2.2.4",
    "electron-rebuild": "^1.8.5",
  • Stack trace (in case of a JS error):
Error: useForm must be used inside of a <Form> component

    in LocationPicker (at FactoryEdit.jsx:130)
    in Component (created by FormDataConsumer)
    in FormDataConsumer (at FactoryEdit.jsx:129)
    in div (created by FormInput)
    in FormInput (created by FormTab)
    in span (created by FormTab)
    in FormTab (created by Context.Consumer)
    in translate(FormTab) (at FactoryEdit.jsx:64)
    in Route (created by Component)
    in div (created by Component)
    in form (created by Component)
    in Component (created by ReactFinalForm)
    in ReactFinalForm (created by TabbedForm)
    in TabbedForm (created by Context.Consumer)
    in withRouter(TabbedForm) (created by FactoryEdit)
    in div (created by ForwardRef(Paper))
    in ForwardRef(Paper) (created by WithStyles(ForwardRef(Paper)))
    in WithStyles(ForwardRef(Paper)) (created by ForwardRef(Card))
    in ForwardRef(Card) (created by WithStyles(ForwardRef(Card)))
    in WithStyles(ForwardRef(Card)) (created by Component)
    in div (created by Component)
    in div (created by Component)
    in Component (created by Edit)
    in Edit (at FactoryEdit.jsx:62)
    in FactoryEdit (created by WithStyles(FactoryEdit))
    in WithStyles(FactoryEdit) (created by WithPermissions)
    in WithPermissions (created by Context.Consumer)
    in Route (created by ResourceRoutes)
    in Switch (created by ResourceRoutes)
    in ResourceRoutes (created by Resource)
    in Resource
    in Route (created by RoutesWithLayout)
    in Switch (created by RoutesWithLayout)
    in RoutesWithLayout (created by Context.Consumer)
    in div (created by Layout)
    in main (created by Layout)
    in div (created by Layout)
    in div (created by Layout)
    in Layout (created by WithStyles(Layout))
    in WithStyles(Layout) (created by Context.Consumer)
    in withRouter(WithStyles(Layout)) (created by ConnectFunction)
    in ConnectFunction (created by LayoutWithTheme)
    in ThemeProvider (created by LayoutWithTheme)
    in LayoutWithTheme (at Layout.jsx:10)
    in CustomLayout (created by ConnectFunction)
    in ConnectFunction (created by Context.Consumer)
    in Route (created by CoreAdminRouter)
    in Switch (created by CoreAdminRouter)
    in div (created by CoreAdminRouter)
    in CoreAdminRouter (created by ConnectFunction)
    in ConnectFunction
    in ConnectFunction (created by Context.Consumer)
    in Route (created by CoreAdmin)
    in Switch (created by CoreAdmin)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (created by Context.Consumer)
    in ConnectedRouterWithContext (created by ConnectFunction)
    in ConnectFunction (created by CoreAdmin)
    in TranslationProviderView (created by ConnectFunction)
    in ConnectFunction (created by CoreAdmin)
    in Provider (created by CoreAdmin)
    in CoreAdmin (at App.jsx:866)
    in App (at src/index.jsx:19)

Most helpful comment

@DataHippo93 I already have react-final-form in my package json.
I am using useForm like this -

//useFormComp.js

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

const UseFormComponent = ({ children }) => {
    const form = useForm();
    console.log("children", children);
    console.log(form.change);
    if (typeof children === 'function')
        return children && children({ change: form.change });
};
export default UseFormComponent;

and using it in my react class component like this--->

<FormDataConsumer>
       {({formData, ...rest}) =>
                 <UseFormComponent>{
                      ({ change }) => (
                            <CaptureButton
                                 uploadForCoverImage={(assetId) => {
                                  change('image', assetId);
                                  }}
                                  {...rest}/>
                                 )
                        }</UseFormComponent>
          }
</FormDataConsumer>

All 4 comments

Hi, and thanks for your question. As explained in the react-admin contributing guide, the right place to ask a "How To" question, get usage advice, or troubleshoot your own code, is StackOverFlow.

This makes your question easy to find by the core team, and the developer community. Unlike Github, StackOverFlow has great SEO, gamification, voting, and reputation. That's why we chose it, and decided to keep GitHub issues only for bugs and feature requests.

So I'm closing this issue, and inviting you to ask your question at:

http://stackoverflow.com/questions/tagged/react-admin

And once you get a response, please continue to hang out on the react-admin channel in StackOverflow. That way, you can help newcomers and share your expertise!

I fixed this by adding react-final-form to my package JSON. For some reason webpack had an issue with it being a peer dependency of ra-core.

@DataHippo93 I already have react-final-form in my package json.
I am using useForm like this -

//useFormComp.js

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

const UseFormComponent = ({ children }) => {
    const form = useForm();
    console.log("children", children);
    console.log(form.change);
    if (typeof children === 'function')
        return children && children({ change: form.change });
};
export default UseFormComponent;

and using it in my react class component like this--->

<FormDataConsumer>
       {({formData, ...rest}) =>
                 <UseFormComponent>{
                      ({ change }) => (
                            <CaptureButton
                                 uploadForCoverImage={(assetId) => {
                                  change('image', assetId);
                                  }}
                                  {...rest}/>
                                 )
                        }</UseFormComponent>
          }
</FormDataConsumer>

I have the same problem with react-admin 3.10.0 and earlier versions
The only way to make it work is by declaring the component using useForm in the same file and before the component generating the Form (in my case my Create component with a SImpleForm).
I am not good enough to find why but I hope it helps.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marknelissen picture marknelissen  路  3Comments

yangjiamu picture yangjiamu  路  3Comments

samanmohamadi picture samanmohamadi  路  3Comments

rkyrychuk picture rkyrychuk  路  3Comments

ilaif picture ilaif  路  3Comments