Passing data to components

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 still is 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) {
    /* ... */
}

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

    SingleProduct(product);
}

This is a terrible piece of pseudo-code, but it illustrates the idea: we want to be able to pass arguments to the SingleProduct function.

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 reliant 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

results matching ""

    No results matching ""