Props

Props 

So, we've seen that we can build up our app a bit like Lego; we get lots of small parts (components) and join them together until we have what we want.  Plus, unlike Lego, React lets us define our own components for extra flexibility. But so far, we've kept all the components static - in other words, they each return the same thing each time; for example, our HelloWorld component always returns <h1>Hello World</h1>.  They've also been totally independent of each other; we haven't passed any information between them.

Which brings us to props.

Props are variables which are passed from a parent component to a child component.

An important thing to understand about props is that the child component cannot change the props which it receives.

For an analogy, think of a car; it has certain properties, such as number of doors, whether or not it has a sunroof etc.  These are the car's props (or properties) - they are set at manufacture, and can't easily be changed (ok, if you know what you're doing you could take a door or two off, or cut a sunroof in it, but basically, on a day-to-day basis and with an average owner, they are things which stay the same).  If I decide to buy a car, I can specify the number of doors etc, and that's what I'll get.  If I later decide I want a 5-door car instead of a 3-door one, I can get that, but only by getting a whole new car.

In the same way, let's say we're creating an app which has 2 buttons which are to be shown in different colours: one needs to be red, and one needs to be blue.  We can create a Button component which accepts a prop called 'colour', and returns a <button> element in that colour.  Then when we need to show a Button on a page, we specify the colour of Button to show, just as we would specify the number of doors when we order a car.

Once our Button is rendered on the page, though, we can't just change its colour* - if we want our blue Button to turn green when it's clicked, for instance, we need to remove the blue one and replace it with a green one instead.  Thankfully React handles this for us, but it's important to understand that the green Button is not the same Button; it doesn't remember anything that the blue Button did (in the same way that you can change your car colour by changing your car, but the new one isn't going to have all your stuff still in the boot).

(*Thankfully, there is a way to change the colour; it's just that props are not the way)


Pass Props to a Component

So let's look at how we can create a component which can receive props.  We'll keep this example very simple - let's create a way to render different coloured messages which will be shown to the user.  We could just do this without making a separate component by rendering a <p> element and styling it each time, but by having a separate component we can make sure that all messages are in the same style (eg maybe we want to draw a border round them all, or change the text to italic or something).

If the message is an error warning, we'll show it in red.  If it's a success message, we'll show it in green.  If it's just normal information that we want to highlight, we'll show it in blue.  Any other messages will be shown in black.


Creating The Component

We'll call our component UserMessage, so create a file called user-message.js and put it in the same directory as App.js. Now we'll temporarily render some placeholder text:

    const UserMessage = () => {
        return <p>Placeholder text</p>;
    }
    export default UserMessage;

Now if you go to App.js, import the new component by adding the following line at the top of the file:

    import UserMessage from './user-message';

and change the return function to this:

    return <UserMessage/>;

and then run your app, you'll see that the browser shows 'Placeholder text'.  All good so far.


Passing In A Prop

Now let's see how we can pass in our message.  For this, the UserMessage component needs to know that it's going to receive some text, and that it should display it.  First we need a name for our prop; we'll call it message.

So, the UserMessage component becomes:

    const UserMessage = (props) => {
        return <p>{props.message}</p>;
    }
    export default UserMessage;

We've told it to expect to receive some props, and that it should then find one called message and display that.  Then, in App.js we pass the message in:

    return <UserMessage message="This is my message"/>;

Save both files and check your browser; it should now say "This is my message".

NOTE: you need to make sure that the parent component (App, in this case) is naming the prop correctly, otherwise the child (UserMessage) won't be able to identify it.


Passing In Multiple Props

We said we also wanted to change the colour of the message, depending on the message type (error, success or info).  So, UserMessage needs to accept a prop called messageType, and use that to determine the colour of the text.  So, it becomes:

    const UserMessage = (props) => {
        let colour;
        switch(props.messageType) {
            case 'error':
                colour = 'red';
                break;
            case 'success':
                colour = 'green';
                break;
            case 'info':
                colour = 'blue';
                break;
            default:
                colour = 'black';
                break;
        }
        return <p style={{color: colour}}
>{props.message}</p>;
    }
    export default UserMessage;

and now in App.js we can specify the message type:

    return <UserMessage message="This is my message" messageType="info"/>

Now the browser should be showing "This is my message" in blue.  You can change the messageType and see that the text changes colour.  If you change it to something that's not error, success or info, it will default to black.

Hopefully you can see how this is helpful when you're building your app - you can create a component which can be configured in slightly different ways for different situations, while still maintaining core functionality and appearance.

Destructuring Props

In the example above, we've passed in props, and then accessed the individual values with props.message and props.messageType.  However, we can also destructure the props array as soon as it's received, and create new variables.  To do this, change

    const UserMessage = (props) => {

to

    const UserMessage = ({message, messageType}) => {

and now, instead of referring to props.message and props.messageType, we can just use message and messageType.


Optional Parameters

What happens if App doesn't pass in both parameters?  Well, if you try removing the message and messageType:

    return <UserMessage/>

you'll see that nothing is shown in the browser, but it doesn't actually throw an error. So in that sense, all props are optional.  However, you can run into difficulty if you're then trying to use the prop in the child component.  There are 3 things that can happen with props:

1. You pass in a valid value.  All is well.

2. You pass in an invalid value (eg message={4} or message={null}).  The results of this can be unpredictable.
If you're just printing the value to the screen, JavaScript will handle it in different ways - numbers will be displayed ok, null or a boolean value will show nothing but not cause any further issues, objects (eg message={{key: 1, value: 'test'}}) will cause an error in the console.
If you're planning to use the value to do some further computation in the child component, all bets are off, but you'll most likely run into issues fairly quickly.

3. You don't pass the parameter at all (eg return <UserMessage />. In this case the value in the child component will be undefined.  Again, this is not necessarily a problem, depending on what you're planning on doing with that value.  You can test for this situation by using if(message === undefined).

Note: Plain React/JavaScript doesn't do anything much in the way of checking what we're passing around, but we can add in something called TypeScript to handle that for us.  We'll cover that in a much later tutorial.

Default Values

Often we want to set a default value for props.  We can do that easily; if we want to make messageType default to 'info', we can add the following line in UserMessage (before the line let colour;):

    const {message = "My Message", messageType = "info"} = props;

Or, if we use destructuring in the function signature, we can change it to:

    const UserMessage = ({message = "My Message", messageType = "info"}) => {

Alternatively, we can define default props by adding a static property to UserMessage (this goes at the bottom of the file, after export default UserMessage):

    UserMessage.defaultProps = {
        message: 'My Message',
        messageType: 'info',
    }

Now if you change App.js to <UserMessage/> (no parameters) you'll see that the browser shows 'My Message' in blue (the colour for info).

Summary

Now we can pass parameters from one component into another.  The key thing to remember is that props must be passed from parent to child, and they cannot be changed by the child component once they've been passed in.

Of course, sometimes we want a component to be able to change something about itself.  We also often want to pass information from a child component to the parent.  Both of these things can be done using state variables, so that's what we'll look at next.

Comments