简体   繁体   中英

How to pass props from different component(about and contact)? not parent to child. Using react typescript

Im trying to reused the about component to contact component. but i can't pass the data using props. when i debug it the props is undefined. and sometime the error is map undefined.

Here is my code

"ABOUT COMPONENT" about.tsx

enum Color {
  Black = 'Black',
  Red = 'Red'
}

type aboutProps = {
items: string, 
backgroundColor: Color, 
minimumHeight: number, 
justify: string, 
align: string, 
fullWidth: boolean
}

const AboutComponent: FC<aboutProps> = ({items, backgroundColor, minimumHeight, justify, align, 
fullWidth}: aboutProps) => { return (
<>
<aboutContainer>
<div>
{items.map(({ heading, url, openInNewWindow, fontColor }) => {
return ( 
    <Text {...props}
    fontColor={fontColor}
    text={heading}
    url={url}
    openInNewWindow={openInNewWindow}                             
    dangerouslySetInnerHTML={{ __html: heading }}/>
    )
})}
</div>
</aboutContainer>
<>
)
}; 
export default AboutComponent;

"CONTACT COMPONENT"
contact.tsx

interface aboutProps {
items: string, 
backgroundColor: Color, 
minimumHeight: number, 
justify: string, 
align: string, 
fullWidth: boolean
}

interface contactProps extends aboutProps {
phone?: number,
name?: string,
}

const ContactComponent: FC<contactProps> = ({items, backgroundColor, minimumHeight, justify, align, fullWidth, phone, name}: contactProps) => { 

return (
    <>
     <AboutComponent items={items}
      backgroundColor = {backgroundColor}
      minimumHeight = {minimumHeight}
      justify = {justify}
      align = {align}
      fullWidth = {fullWidth}/>
    </>
)

};

export default ContactComponent;

The props is undefined such as items, background, minimumHeight, justify, align, fullWidth

It is a parent-child relationship. I don't know why you think it's not. ContactComponent is the parent and AboutComponent is the child.

ContactComponent requires that it be called with all of the props of AboutComponent and you are passing them down correctly. If you are getting errors that the props are undefined then that problem isn't in the code you've posted here. It's that you are calling <ContactComponent/> without the right props.


But there are a lot of other problems. The most glaring one is the prop items -- your types say that it is a string , but your code treats it as an array of objects with properties { heading, url, openInNewWindow, fontColor } . You need to either A) require that you have an array of these objects or B) use the children prop and call your AboutComponent with <Text/> elements inside of it.

You don't want to use dangerouslySetInnerHTML and you really don't need it. It doesn't do anything here unless your Text component is taking this prop and using it. It looks like your Text is already taking a text prop so it can create its own HTML content.

You might want to make some of the style props optional. Right now they are all required.

This is my attempt to clean-up your components:

import React, { CSSProperties, FC } from "react";

export enum Color {
  // CSS color names are lower-case
  Black = "black",
  Red = "red"
}

export type TextProps = {
  text: string;
  url: string;
  openInNewWindow?: boolean;
  fontColor?: CSSProperties["color"];
};

export const Text: FC<TextProps> = ({
  text,
  url,
  openInNewWindow = false,
  fontColor
}) => {
  return (
    <a
      href={url}
      target={openInNewWindow ? "_blank" : "_self"}
      style={{ color: fontColor, display: "block", padding: 10 }}
    >
      {text}
    </a>
  );
};

export type AboutContainerProps = {
  backgroundColor?: Color;
  minimumHeight?: number;
  justify?: string;
  align?: string;
  fullWidth?: boolean;
};

export const AboutContainer: FC<AboutContainerProps> = ({
  backgroundColor = Color.Black,
  minimumHeight,
  justify = "space-evenly",
  align = "center",
  fullWidth = true,
  children // the JSX children -- this prop is automatically included by FC
}) => {
  return (
    <div
      style={{
        background: backgroundColor,
        minHeight: minimumHeight,
        width: fullWidth ? "100%" : "auto",
        display: "flex",
        justifyContent: justify,
        alignItems: align
      }}
    >
      {children}
    </div>
  );
};

export type AboutProps = AboutContainerProps & {
  items: TextProps[];
};

export const AboutComponent: FC<AboutProps> = ({
  items,
  ...aboutProps // rest object for all other props
}: AboutProps) => {
  return (
    <AboutContainer {...aboutProps}>
      {items.map((props) => {
        return <Text key={props.text} {...props} />;
      })}
    </AboutContainer>
  );
};

export type ContactInfo = {
  phone?: number;
  name?: string;
};

export const ContactInfoComponent: FC<ContactInfo> = ({ phone, name }) => {
  if (!phone && !name) {
    return null;
  }
  //TODO: format phone number string
  return (
    <div>
      <h3>Get In Touch</h3>
      {name && <div>{name}</div>}
      {phone && <div>Call Me: {phone}</div>}
    </div>
  );
};

export type ContactProps = AboutProps & ContactInfo;

// do we really even need this component?  can't we just compose it from the other two?
export const ContactComponent: FC<ContactProps> = ({
  phone,
  name,
  ...aboutProps
}) => {
  return (
    <div>
      <ContactInfoComponent phone={phone} name={name} />
      <AboutComponent {...aboutProps} />
    </div>
  );
};

Code Sandbox Link

With that sort of setup you call it like this, where items is an array of props objects.

const Test = () => (
  <ContactComponent
    minimumHeight={100}
    justify="flex-end"
    align="center"
    items={[
      {
        text: "Home",
        url: "/"
      },
      {
        text: "Facebook",
        url: "some link",
        openInNewWindow: true,
        fontColor: Color.Red
      }
    ]}
    name={"My Name"}
    phone={5555555555}
  />
);

This makes sense if the data is coming in a raw form from a database or API. But if you are writing out props yourself you probably want to call it using modular pieces:

const Test = () => (
  <div>
    <ContactInfoComponent name={"My Name"} phone={5555555555} />
    <AboutContainer
      minimumHeight={100}
      justify="flex-end"
      align="center"
      fullWidth={false}
    >
      <Text url="/">Home</Text>
      <Text url="some link" openInNewWindow={true} fontColor={Color.Red}>
        Facebook
      </Text>
    </AboutContainer>
  </div>
);

So you would make use of the children prop rather than passing props down multiple levels.

import React, { CSSProperties, FC } from "react";

export enum Color {
  // CSS color names are lower-case
  Black = "black",
  Red = "red"
}

export type TextProps = {
  url: string;
  openInNewWindow?: boolean;
  fontColor?: CSSProperties["color"];
};

export const Text: FC<TextProps> = ({
  children,
  url,
  openInNewWindow = false,
  fontColor
}) => {
  return (
    <a
      href={url}
      target={openInNewWindow ? "_blank" : "_self"}
      style={{ color: fontColor, display: "block", padding: 10 }}
    >
      {children}
    </a>
  );
};

export type AboutContainerProps = {
  backgroundColor?: Color;
  minimumHeight?: number;
  justify?: string;
  align?: string;
  fullWidth?: boolean;
};

export const AboutContainer: FC<AboutContainerProps> = ({
  backgroundColor = Color.Black,
  minimumHeight,
  justify = "space-evenly",
  align = "center",
  fullWidth = true,
  children // the JSX children -- this prop is automatically included by FC
}) => {
  return (
    <div
      style={{
        background: backgroundColor,
        minHeight: minimumHeight,
        width: fullWidth ? "100%" : "auto",
        display: "flex",
        justifyContent: justify,
        alignItems: align
      }}
    >
      {children}
    </div>
  );
};

export type ContactInfo = {
  phone?: number;
  name?: string;
};

export const ContactInfoComponent: FC<ContactInfo> = ({ phone, name }) => {
  if (!phone && !name) {
    return null;
  }
  //TODO: format phone number string
  return (
    <div>
      <h3>Get In Touch</h3>
      {name && <div>{name}</div>}
      {phone && <div>Call Me: {phone}</div>}
    </div>
  );
};

This code is mostly the same but we can actually delete a few of the components.

Code Sandbox Link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM