reactjstypescriptformsnext.jsscalable

I can't get this map method to print the components that I want to


I'm trying to make an scalable Custom Form from an array of objects and a custom Input field, i'm working on a React-Typescript project

import React, { ChangeEvent } from "react"
import { InputField } from "./InputField"

interface FormItem {
    key: string
    fieldType: string
    placeholder: string
    value: string
    errorMessage: string | null
    validationMethod: (validateValue : string) => string | null 
}

interface FormProps {
    formProps : FormItem[]
}

export const CustomForm = ({formProps} : FormProps) => {
    const [FORM, SETFORM] = React.useState(formProps)


    const onFormChange = (event: ChangeEvent<HTMLInputElement>) => {
        const target = event.currentTarget
        const findChangedValue = FORM.find(fv => fv.key === target.name)
        const validationText = findChangedValue?.validationMethod(target.value)!
        const newForm = FORM.map(fv => {
            if( target.value === fv.key ){
                return {...fv, value: target.value, errorMessage: validationText}
            }
            return fv
        })

        SETFORM(newForm)

    }

    return (
            <>
                {FORM.map(( fv ) => {
                        <InputField 
                            placeholderText={fv.placeholder}
                            name={fv.key} 
                            inputType={fv.fieldType} 
                            handleChange={onFormChange}/>
                })}
            </>
    )
}

And I'm calling this component from a parent component sending this as a prop


interface FormProps {
    key: string
    fieldType: string
    placeholder: string
    value: string
    errorMessage: string | null
    validationMethod: ((validateValue : string) => string | null)
}

    const FORM : FormProps[] = [
        { key: "email", placeholder:"Enter your email", fieldType: "text", value: "", errorMessage: "", validationMethod: validateEmail },
        { key: "password", placeholder:"Enter your password", fieldType: "text", value: "", errorMessage: "", validationMethod: validatePassword },
        ]


    return (
        <StyledForm enabledSubmit={enableSubmit}>
            <h2>Login</h2>
            <CustomForm formProps={FORM}/>
        </StyledForm>
    )

I tried console logging the props on CustomForm and the values are getting there just fine but the InputFields won't appear when i run the project, I also tried to console log the incoming props in the InputField component and they wont print, its like they get lost after passing them down in the map method, and I also tried wrapping the map method between form tags


Solution

  • You're not returning anything from map. You also need to use the key prop and provide a unique key.

          {FORM.map((fv) => (
            <InputField
              key={fv.key}
              placeholderText={fv.placeholder}
              name={fv.key}
              inputType={fv.fieldType}
              handleChange={onFormChange}
            />
          ))}
    

    The curly brackets are changed to round brackets.

    Round brackets (or no brackets) are an implicit return.

    () => ("returning a value")

    () => "returning a value"

    Curly brackets execute the closure. ie. they require a return statement to return a value.

    () => { "doing nothing" }

    () => { return "returning a value" }

    Interestingly, the only reason you don't see an error is because you have the map wrapped in a fragment, which I guess allows a void child node. If you change the fragment to a div, you'll see the error Type 'void[]' is not assignable to type 'ReactNode'.


    Could I also suggest you clean up your type definitions?

    Instead of three interfaces, you can just have one type:

    export type FormItem = {
        key: string
        fieldType: string
        placeholder: string
        value: string
        errorMessage: string | null
        validationMethod: (validateValue : string) => string | null 
    }
    
    const FORM : FormItem[] = [...]
    
    export const CustomForm: FC<{ formProps: FormItem[] }> = ({ formProps }) => {...}