How to return Component from HOC with inline function wrapper?

237 Views Asked by At

I am trying to create an input field, if a place inside <Formik> should act as a Formik Field, but if no Formik is available, then it should act as a general input field. So far I have managed to this using useFormikContext and useField inside the High Order Component (HOC). But I am facing another issue, which actually lead me here, I want to return WrapperComponent with some data, like if field have errors, then I can receive where I am using WrapperComponent.

withField.tsx (HOC)

import { useField, useFormikContext } from "formik";

export const withField = (WrappedComponent: React.ComponentType) => {
const displayName =
    WrappedComponent.displayName || WrappedComponent.name || "Component";

const ComponentWithOrWithoutFormik = (props: any) => {
    const formikContext = useFormikContext();

    if (formikContext) {
        const [field, meta] = useField(props);
        return (
            <WrappedComponent
                {...field}
                {...props}
                onChange={(e: any) => {
                    field.onChange(e);
                    props.onChange && props.onChange(e);
                }}
                onBlur={(e: any) => {
                    field.onBlur(e);
                    props.onBlur && props.onBlur(e);
                }}

            />
        );
    }

    return <WrappedComponent {...props} />;
};

ComponentWithOrWithoutFormik.displayName = `withOrWithoutFormik(${displayName})`;

return ComponentWithOrWithoutFormik;
};

In other file:

const CheckBoxField = withField((props) => <input {...props} />);

And currently I am using it like this:

<CheckBoxField id={`id_${name}`} type="checkbox" name={name} />

But what I actually want to do is use it like this:

<CheckBoxField id={`id_${name}`} type="checkbox" name={name}>
{({errors, touched})=>(
   // something I can do here
)}
</CheckBoxField>

I can do this with simple component by simply returning children like this:

const SomeComponent = ({children, ...props}) => {
   return children(props)
}

But have no idea, how to do it with HOC.

1

There are 1 best solutions below

0
thezeeshantariq On

So after, many trials and errors, the answer was using RenderProps, and this is how I have done it:

import { ErrorMessage, useField, useFormikContext } from "formik";

export const withField = (WrappedComponent: React.ComponentType) => {
const displayName =
    WrappedComponent.displayName || WrappedComponent.name || "Component";

const ComponentWithOrWithoutFormik = (props: any) => {
    const formikContext = useFormikContext();
    const { children, ...rest } = props;

    if (formikContext) {
        const [field, meta] = useField(props);
        return children({
            FormControl: (inputProps: any) => (
                <>
                    <WrappedComponent
                        {...field}
                        {...rest}
                        {...inputProps}
                        onChange={(e: any) => {
                            field.onChange(e);
                            props.onChange && props.onChange(e);
                        }}
                    />
                </>
            ),
            ErrorMessage: () => (
                <ErrorMessage
                    name={props.name}
                    component="div"
                    className="text-error text-xs"
                />
            ),
            touched: meta.touched,
            error: meta.error,
        });
    }

    return children({
        FormControl: () => <WrappedComponent {...props} />,
        meta: undefined,
        touched: undefined,
        error: undefined,
    });
};

ComponentWithOrWithoutFormik.displayName = `withOrWithoutFormik(${displayName})`;

return ComponentWithOrWithoutFormik;

};