wick3d
wick3d

Reputation: 1392

How to pass props to {children} and use it?

I am trying to make a reusable react input element. React version: "react": "17.0.2"

I want to pass htmlFor in the label and use the same in the children id property. So I am trying to pass props to {children} in react. I have tried passing props from this solution

      {React.cloneElement(children, { id: label })}

My component that uses children.

import clsx from 'clsx';
import React from 'react';
import { FunctionComponent } from 'react'; // importing FunctionComponent

type FieldWrapperProps = {
  children: React.ReactNode;
  label: string;
  errorMessage?: string;
  className?: string;
};

export const FieldWrapper: FunctionComponent<FieldWrapperProps> = (props: FieldWrapperProps) => {
  const { children, label, errorMessage, className } = props;
  return (
    <div
      className={clsx('mt-6  flex flex-col justify-center tracking-wide align-middle', className)}
    >
      <label htmlFor={label} className="block text-gray-light text-base  font-bold mb-2 peer">
        {label}
      </label>
      {React.cloneElement(children, { id: label })}
      {/* {children} */}
      <p className="text-sm text-red-500 ">{errorMessage}</p>
    </div>
  );
};

I am using this FieldWrapper in various other form elements like so.

import { UseFormRegisterReturn } from 'react-hook-form';

import { FieldWrapper } from './FieldWrapper';

type InputFieldProps = {
  label: string;
  type: string;
  registration?: UseFormRegisterReturn;
  error?: string;
  placeholderText?: string;
  className?: string;
};

export const InputField = (props: InputFieldProps) => {
  const { type, label, registration, error, placeholderText, className } = props;
  return (
    <FieldWrapper label={label} errorMessage={error} className={className}>
      <input
        type={type}
        className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
        id={label}
        placeholder={placeholderText}
        {...registration}
      />
    </FieldWrapper>
  );
};

There is a type warning too. type error

Upvotes: 2

Views: 11732

Answers (1)

Dan
Dan

Reputation: 10538

You're getting this type error because React.cloneElement() requires an Element to clone; children is not guaranteed to be an element. It's a ReactNode, which is a type alias that includes undefined and null. Those are valid types to return from a React component, but not to pass to cloneElement. You could do this instead:

children = React.Children.map(children, el => {
  return React.cloneElement(el, { id: label })
})

This would allow you to clone each element in turn. If you only expect a single element, React.Children.only() can be used in a similar way

children = React.Children.only(children)
children = React.cloneElement(children)

Upvotes: 4

Related Questions