Passing data to a component

Let's take a look at the App component we've created earlier:

const App: React.FC = () => {
    const product = {
      name: "JavaScript: The Definitive Guide",
      description: `Since 1996, JavaScript: The Definitive Guide...`,
      price: 28.89
    }

    return (
        <section>
            <h1>Hello DevMeetings!</h1>

            <div>
                <header>{product.name}</header>
                <p>
                    {product.description}
                </p>
                <span>{product.price}$</span>
            </div>
        </section>
    )
}

It's simple, but there is still some complexity:

  • some (primitive) state inside,

  • title displayed

  • product displayed

First target for extraction should be the code that is responsible for displaying a product.

The product component

After some copy-pasting, here's the extracted SingleProduct component:

const SingleProduct: React.FC = () => {
    return (
        <div>
            <header>{ product.name }</header>
            <p>
                { product.description }
            </p>
            <span>{ product.price }$</span>
        </div>
    )
}

The problem here is of course that the product variable is in the App component and undefined in the SingleProduct component. We need to be able to pass data from a parent (App) to the child (SingleProduct). If we were talking about functions, and not components it would look more or less like that:

function SingleProduct(product: Product) {
    return /* ... */
}

function App() {
    const product = { /* ... */ };

    return SingleProduct(product);
}

This illustrates the idea: we want to be able to pass arguments to the SingleProduct function.

function App() {
    const product = { /* ... */ };

    return <SingleProduct product={product}/>;
}

Props

The proper way of passing data down the component tree (from parent to child) is through props. They embody the same concept as function above, just use different syntax:

<Component foo={42} bar={"Hello, Devmeetings"} />

That's it - we just passed two pieces of data into the Component: foo that is equal to 42, and bar: the string "Hello, Devmeetings".

Let's get back to the function analogy for a second. Defining it like this would make no sense:

function Component(foo: number, bar: string) {
    /* ... */
}

In this case we are entirely depended on the order of arguments, and their names don't matter event though we typed them. This is much better:

function Component(props: { foo: number; bar: string; }) {
    /* ... */
}

Component with props

This is exactly what happens in React:

const SingleProduct: React.FC = (props: { product: Product }) => {
    return (
        <div>
            <header>{ props.product.name }</header>
            <p>
                { props.product.description }
            </p>
            <span>{ props.product.price }$</span>
        </div>
    )
}

We can even improve the component's code, so it's easier to use:

export type SingleProductProps = { product: Product };

const SingleProduct: React.FC<SingleProductProps> = ({ product }) => {
    return (
        <div>
            <header>{ product.name }</header>
            <p>
                { product.description }
            </p>
            <span>{ product.price }$</span>
        </div>
    )
}

Much better! With couple of simple changes, we gained:

  • understanding that the React.FC is a generic type we got better type safety and IDE support,

  • by destructuring the props argument simplifies code inside the component,

  • by extracting the SingleProductProps into a type we got a minimalistic documentation

Resources

Last updated