React-Typescript: Reku znak JSX očakáva, že jeho vlastnosť 'children' bude jediný potomok typu 'Element | undefined', ale bolo poskytnutých viac potomkov


Používam react-typescript pre svoju aplikáciu. Na styling používam Style component. Rozhodol som sa, že si vytvorím vlastné Hooks Input a Password Input. Najprv som vytvoril Input wrapper, kde som pridal potrebné vstupné polia. Potom som vytvoril pole na zadávanie hesla. Rozšíril som svoje komponentné propty Password Input o Input component prop. Ale keď používam Input component wrapper na heslovú komponentu. Dostávam chybu: Prop 'children' tohto JSX tagu očakáva jedného potomka typu 'Element | undefined', ale bolo poskytnutých viacero potomkov.

Neviem, čo robím zle.

Toto je moja vstupná obalová komponenta

import React from "react";
import styled from "styled-components";
import ErrorBoundary from "components/errorBoundary";

const Container = styled.div`
  position: relative;
`;

const Label = styled.label<{ minimized?: boolean }>`
  display: block;
  color: ${({ theme }) => theme.inputLabelColor};
  font-weight: 400;
  font-size: ${({ minimized }) => (minimized ? "11px" : "16px")};
  margin-bottom: 5px;
  letter-spacing: -0.2px;
  position: absolute;
  transform: translateY(${({ minimized }) => (minimized ? "9px" : "16px")});
  left: 20px;
  top: 0;
  pointer-events: none;
  transition: transform 150ms linear, font-size 150ms linear;
`;

const Error = styled.p`
  color: ${({ theme }) => theme.errorTextColor};
`;

const Description = styled.p`
  color: blue;
`;

export interface IInputWrapperProps {
  label?: string;
  required?: boolean;
  minimizedLabel?: boolean;
  description?: string;
  error?: string;
  wrapperStyle?: React.CSSProperties;
  children?: JSX.Element;
}

export default ({
  label,
  error,
  description,
  children,
  required,
  wrapperStyle,
  minimizedLabel
}: IInputWrapperProps) => {
  return (
    <ErrorBoundary id="InputWrapperErrorBoundary">
      <div style={wrapperStyle}>
        <Container>
          <Label minimized={minimizedLabel}>
            {label} {required && <span style={{ color: "red" }}> *</span>}
          </Label>
          {children}
        </Container>
        {description && <Description>{description}</Description>}
        {error && <Error>{error}</Error>}
      </div>
    </ErrorBoundary>
  );
};

Toto je moja komponenta hesla

import React, { useState } from "react";
import styled from "styled-components";
import eyeHide from "./eye-svgfiles/eyeHide.svg";
import eyeShow from "./eye-svgfiles/eyeShow.svg";
import InputWrapper, { IInputWrapperProps } from "../wrapper";
const Passwordinput = styled.input`
  border: 2px solid ${({ theme }) => theme.inputBorderColorFaded};
  background: ${({ theme }) => theme.inputBackgroundColor};
  display: block;
  border-radius: 5px;
  box-shadow: none;
  color: ${({ theme }) => theme.inputTextColor};
  height: 52px;
  width: 100%;
  margin: 0;
  padding: 1px 15px;
  &:focus {
    border: 2px solid ${({ theme }) => theme.inputBorderColor};
    outline: 0;
  }
`;
const Svg = styled.div`
  position: absolute;
  left: 50%;
  top: 65%;
  transform: scale(0.8);
`;

export interface IPasswordProps extends IInputWrapperProps {
  onChange: (i: string) => void;
  value?: string | undefined;
}
export default ({ onChange, value, label, error, ...rest }: IPasswordProps) => {
  const [state, setstate] = useState(false);
  return (
    <div>
      <InputWrapper label={label} error={error} {...rest}> //I am getting error in here 
        <Passwordinput
          label={label}
          type={state ? "text" : "password"}
          onChange={e => onChange(e.target.value)}
          value={value}
          error={error}
        />
        <Svg>
          <img
            onClick={() => setstate(state => !state)}
            style={{ cursor: "pointer" }}
            src={state ? eyeShow : eyeHide}
            alt="searchIcon"
          />
        </Svg>
      </InputWrapper>
    </div>
  );
};

Odpoveď č. 1

Napíšte svoje rozhranie takto:

export interface IInputWrapperProps {
  label?: string;
  required?: boolean;
  minimizedLabel?: boolean;
  description?: string;
  error?: string;
  wrapperStyle?: React.CSSProperties;
  children?: JSX.Element|JSX.Element[];
}

v skutočnosti písanie:

children: JSX.Element|JSX.Element[];

Deti môžu byť buď jediným JSX prvkom, alebo poľom prvkov.

Odpoveď č. 2

Všetkým používateľom React 18

Ak ste práve upgradovali na React 18 a ste ako ja, všade pridávate React.PropsWithChildren<>, pretože typy pre props už nevypovedajú o children. Ak ste našli tento thread, pravdepodobne ste narazili na to, na čo som narazil aj ja, kde jeden náhodný komponent dal túto chybu zdanlivo bez dôvodu, aj keď je úplne legálne mať viacero prvkov ako children.

RIEŠENIE:

Veľa rôznych vecí môže byť „react children“, vrátane mnohých primitívnych typov, a veci ako undefined, null a false sa jednoducho nezobrazia. Moja komponenta bola niečo takéto:

<Container>
  {maybeNull && <Component notNullThing={maybeNull} />}
  {maybeNull2 && <Component notNullThing={maybeNull2} />}
  {maybeNull3 && <Component notNullThing={maybeNull3} />}
</Container>

Ukázalo sa, že maybeNull mal technicky typ unknown, a to všetko pokazilo. Opravilo som to pomocou <>{maybeNull && <Component notNullThing={maybeNull} />}</> (pretože jediný unknown je z nejakého dôvodu legálny), hoci som mohol použiť aj {maybeNull ? <Component notNullThing={maybeNull} /> : null} alebo niečo podobné.

Myslím si, že je to preto, že typ React.ReactNode je veľká unia, ktorá zahŕňa ReactFragment, ktorý je rekurzívne implementovaný ako Iterable<ReactNode>. Preto môže byť ReactNode poľom ReactNode-ov. Avšak unknown znamená, že neviete, ktorá z vecí v unii to je, takže neviete, že by to mohlo byť Iterable<ReactNode>. Niečo také TypeScripty.

Odpoveď č. 3

Dá sa to opraviť pomocou:

interface InputWrapperProps {
  ...yourProps,
  children?: React.ReactNode
}

Dúfam, že to pomôže!

Odpoveď č. 4

Pre mňa variant navrhnutý shuhat36 funguje dobre. Tak som len pridal fragment pre deti.

Príklad s chybou:

<div className="container">
   {renderTabs()}
   {renderButtons()}
</div>

A ten istý kód, ale opravený:

<div className="container">
   <>
      {renderTabs()}
      {renderButtons()}
   </>
</div>

Súvisiace články