IFormContext only refers to a type, but is used as a value here

I am trying to build a validation for a ionic react form. Before I do that I need to get the state values of the input. I have a field component, which describes the fields, a form structure component and the form page which requires both of the above components. Below is my code for the form fields:

import React, {FormEvent} from "react";
import { IErrors } from "./ResetPassword";
import { IonInput} from "@ionic/react";

type Editor = "textbox";

export interface IFieldProps {
    id: string;
    name: string;
    label?: string;
    editor?: Editor;
    onIonChange?: any;
    value?: any;
    ionChangeValue?: string;
}

export const ResetField: React.FunctionComponent<IFieldProps> = ({
  id,
  name,
  label,
  editor,
  value,
  onIonChange
})=>{
    return (
        <div className="form-group">
            {label && <label htmlFor={id}>{label}</label>}
            {editor!.toLocaleLowerCase()=== "textbox" && (
                <IonInput
                  id={id}
                  name={name}
                  type="password"
                  value={value}
                  onIonChange={onIonChange}
                  onBlur={()=>
                      (e: FormEvent<HTMLIonInputElement>)=> 
                          console.log(e)
                  }
                  className="form-control"
                  />
            )}
        </div>
    )
};

ResetField.defaultProps = {
    editor: "textbox"
};

Below is the code for the form structure:

import * as React from "react";
import { IonPage, IonHeader, IonInput, IonItem, IonContent, IonLabel, IonToolbar, IonTitle, IonButton } from "@ionic/react";
import '../css/app.scss';
import { ResetField } from "./resetFormFields";



interface IFormProps {
    render: () => React.ReactNode;
}

export interface IFormContext extends IFormState {
    /* Function that allows values in the values state to be set */
    setValues: (values: IValues) => void;
  }
  /*
   * The context which allows state and functions to be shared with Field.
   * Note that we need to pass createContext a default value which is why undefined is unioned in the type
   */
  export const FormContext =
    (React.createContext < IFormContext) | (undefined > undefined); //<--- I am getting an error saying 'IFormContext' only refers to a type, but is used as a value here



export interface IValues {
    [key: string]: any;
}

export interface IErrors {
    [key: string]: string;
}

export interface IFormState {
    values: IValues;
    errors: IErrors;
    submitSuccess?: boolean;
}

export interface IFormContext extends IFormState {
    
    setValues: (values: IValues) => void;
  }

export class Reset extends React.Component<IFormProps, IFormState> {

    
  constructor(props: IFormProps) {
      super(props);

      const errors: IErrors = {};
      const values: IValues = {};
      this.state = {
          errors,
          values
      };
     
  }

  private haveErrors(errors: IErrors) {
    let haveError: boolean = false;
    Object.keys(errors).map((key: string)=> {
        if(errors[key].length > 0){
            haveError = true;
        }
    });
    return haveError;
}

 private handleSubmit = async (
     e: React.FormEvent<HTMLFormElement>): Promise<void> => {
         e.preventDefault();
         
         if(this.validateForm()){
             const submitSuccess: boolean = await this.submitForm();
             this.setState({submitSuccess});
         }

         console.log(this.state.values);
     }

    private validateForm(): boolean {
        return true;
    }

    private async submitForm(): Promise<boolean> {
        return true;
    }

    public render() {

        const { submitSuccess, errors } = this.state;
        return (
            <IonPage id="login-registration-page">
            <IonHeader>
              <IonToolbar color="primary">
                <IonTitle>Reset Password</IonTitle>
              </IonToolbar>
            </IonHeader>
            <IonContent>
            <form onSubmit={this.handleSubmit} className="login-registration-form ion-padding" noValidate={true}>
              
                <div className="container">
                    {this.props.render()}
                    <div className="form-group">
                        <IonButton
                           type="submit"
                           disabled={this.haveErrors(errors)}
                           expand="block"
                           >Submit</IonButton>
                    </div>
                    {submitSuccess && (
                        <div className="alert alert-info" role="alert">
                          The form was successfully submitted!!
                        </div>
                    )}
                    {submitSuccess === false && 
                          !this.haveErrors(errors) && (
                              <div className="alert alert-danger" role="alert">
                                  Sorry, an unexpected error has occured
                            </div>
                    )}
                    {submitSuccess === false &&
                       this.haveErrors(errors) && (
                           <div className="alert alert-danger" role="alert">
                             Sorry, the form is invalid. Please review adjust and try again
                            </div>
                       )}
                </div>
            </form>
            </IonContent>
            </IonPage>
        )
    }
}

And below is my resetForm page code:

import React, {useState} from "react";
import { Reset } from "../components/ResetPassword";
import { ResetField } from "../components/resetFormFields";
import { RouteComponentProps } from 'react-router-dom';
import { IonInput} from "@ionic/react";
import '../css/app.scss';

interface OwnProps extends RouteComponentProps {}

interface ResetProps extends OwnProps {};


export const ResetForm: React.FunctionComponent<ResetProps> = () => {
    const [inputCurrentPassword, setCurrentPassword] = useState('');
        const [inputNewPawword, setNewPassword] = useState('');
        const [inputConfirmPassword, setConfirmPassword] = useState('');
    return (
    <Reset
          render={()=> (
              <React.Fragment>
                  <div className="alert alert-success" role="alert">
                      Reset your password to activate your account
                  </div>
                  <ResetField id="currentPassword" name="currentPassword" label="Current Password:" value={inputCurrentPassword} onIonChange={(e: { detail: { value: any; }; }) => setCurrentPassword(e.detail.value!)}/>
                  <ResetField id="newPassword" name="newPassword" label="New Password:" value={inputNewPawword} ionChangeValue="setNewPassword" onIonChange={(e: { detail: { value: any; }; }) => setNewPassword(e.detail.value!)}/>
                  <ResetField id="retypePassword" name="retypePassword" label="Confirm Password:" value={inputConfirmPassword} ionChangeValue="setConfirmPassword" onIonChange={(e: { detail: { value: any; }; }) => setConfirmPassword(e.detail.value!)}/>
              </React.Fragment>
          )}
          />
   )
   
}

In the form structure code, I getting the error saying 'IFormContext' only refers to a type, but is used as a value here. I don’t understand the issue here. I get that since IFormContext is an interface, it is a type here. I wouldn’t have issues if it were a class. But it needs to be an interface here.

Answer

Your error is due to some misplaced parentheses. Nothing to do with types vs. interfaces vs. classes, that’s all fine.

export const FormContext = (React.createContext < IFormContext) | (undefined > undefined); 

needs to be fixed to

export const FormContext = React.createContext<IFormContext | undefined>(undefined); 

Leave a Reply

Your email address will not be published. Required fields are marked *