Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).

Props

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

So far, the component is 100% static. What I mean is, there are no variables at all. No matter what, each time we render RepLogApp, we will get the exact same result.

I said earlier that a React component is kind of like a PHP class. Well, when we instantiate a class in PHP, we can pass in different arguments to make different instances of the same class behave in different ways.

And... yea! We can do the exact same thing here: we can pass variables to a component when it's rendered, and use those in the render() method.

Components are Instantiated

But, first, a quick note: when you use the JSX syntax for a component, React instantiates a new instance of our class. Yep, we can actually render multiple RepLogApp components on the page, and each of them will be a separate object. with separate data.

Passing in a Prop

It turns out, that's hugely powerful. Suppose that we want to be able to render our RepLogApp in multiple places on the page at the same time. But, sometimes we want the heart Emoji, but sometimes we don't. To make that possible, we need to be able to pass a flag to RepLogApp that tells it whether or not to render the heart.

Inside rep_log_react.js, create a new const shouldShowHeart = true. I'll also update the render code to use multiple lines, just to keep things clear.

... lines 1 - 4
const shouldShowHeart = true;
... lines 6 - 11

So, how can we pass this variable into RepLogApp? By adding what looks like an attribute: withHeart - I'm just making that name up - equals {shouldShowHeart}.

... lines 1 - 6
render(
<RepLogApp withHeart={shouldShowHeart} />,
document.getElementById('lift-stuff-app')
);

The JSX {JavaScript} Syntax

Woh. Wait. Something crazy just happened. We are inside of JSX on this line. And, because JSX is like HTML, we know that we could, of course, say something like withHeart="foo". That's true, but whenever you're in JSX, if you write {}, that puts you back into JavaScript mode! Once inside, You can write literally any valid JavaScript: like reference the shouldShowHeart variable or even add expressions. We'll do this all the time.

Reading Props from Inside a Component

Now, I referred to withHeart as an "attribute". But, in React, this is actually known as a prop, and its the way that you pass "arguments" or "data" into your component. Inside RepLogApp, we can access any props that were passed to us via a this.props property.

Check this out: in render() create a new variable called heart and set it to empty quotes. Then, if this.props.withHeart - referencing the prop we passed in - say heart =, copy the span JSX from below, and paste it here.

... lines 1 - 2
export default class RepLogApp extends Component {
render() {
let heart = '';
if (this.props.withHeart) {
heart = <span>❤️</span>;
}
... lines 9 - 12
}
}

Oh, and notice that when we use this.props.withHeart, we have an error from ESLint about some missing prop validation. That's just a warning, and we're going to talk about it later. For now, totally ignore it.

Below, I want to break my return statement onto multiple lines. You can use multiple lines to define JSX, as long as you surround it with parenthesis. I do this a lot for readability.

Finally, instead of the span, we want to print the heart variable. How? Use {heart}. Based on the value of the prop, this will print an empty string or a React element.

... lines 1 - 9
return (
<h2>Lift Stuff! {heart}</h2>
);
... lines 13 - 15

Right now, withHeart is equal to true. So let's see if this work: find your browser and refresh! Yes! We still see the heart! Change shouldShowHeart to false and try it again. The heart is gone!

Rendering a Component Multiple Times

To really show off, change that value back to true, but let's see if we can render RepLogApp multiple times. Copy the JSX, paste, and set withHeart to false.

... lines 1 - 6
render(
<RepLogApp withHeart={shouldShowHeart} /> <RepLogApp withHeart={false} />,
document.getElementById('lift-stuff-app')
);

But, as soon as we do this, the Webpack build fails! Find your terminal to see what it's complaining about:

Syntax Error: Adjacent JSX elements must be wrapped in an enclosing tag

This is less scary than it sounds. It's not that you can't put components next to each other like this, it just means that there must be just one element all the way at the top of our JSX tree. Each component also needs to follow this rule. And, RepLogApp already is: it has one top-level element: the h2.

To put just one element at the top of our element tree, there's a simple fix: add a div and render both components inside. Oh, and I completely forgot to use {} around my "false" - false is JavaScript.

... lines 1 - 6
render(
<div>
<RepLogApp withHeart={shouldShowHeart} />
<RepLogApp withHeart={false} />
</div>,
document.getElementById('lift-stuff-app')
);

Now that Webpack is happy again, go back and refresh! Sweet! Our component is rendered twice: each is its own object with its own data.

Props are just about the most important concept in React, and they will be the key to us creating killer UI's that update dynamically.

Back in rep_log_react.js, we don't really need two of these components: so go back to just one. And, beautiful!

It's time to build out the rest of our app: first, by moving the table into RepLogApp.

Leave a comment!

4
Login or Register to join the conversation
Mickael-M Avatar
Mickael-M Avatar Mickael-M | posted 2 years ago

Hi guys, can we type the variable ? Like php : public bool isTrue. Doing the same with shouldShowHeart : const bool shouldShowHeart ?

Reply

Hey Micka,

Unfortunately, you can't. See syntax of "const" statement: https://developer.mozilla.o... - it just "%name% = %value%". We're talking about simple variables here with "const"... in your PHP example you're talking about class properties :) And fairly speaking you don't need to typehint "const" because you can set its value only once. If you need a bool const - set a bool value and that's it. You will be 100% sure it's bool further in the code as you can't re-set it because it's a constant.

Cheers!

Reply
Dan_M Avatar
Dan_M Avatar Dan_M | posted 4 years ago | edited

Hey guys!

So we can pass a prop from rep_log_react.js to RepLogApp, but what's the right way to pass that prop from our twig template to rep_log_react.js?

I did it with this in my controller:


        return $this->render('lift/index.html.twig', array(
            'leaderboard' => $this->getLeaders($replogRepo, $userRepo),
            'shouldShowHeart' => true,
        ));

This in my template:


        <div id="lift-stuff-app" data-should-show-heart={{shouldShowHeart}}></div>

And this in rep_log_react.js:


const shouldShowHeart = document.querySelector('#lift-stuff-app').dataset.shouldShowHeart;

render(
    <RepLogApp withHeart={shouldShowHeart}/>,
    document.getElementById('lift-stuff-app')
);

That works, but it doesn't look hipster enough to belong in the React world. Is there a better way?

Thanks!

Reply

Hey Dan_M!

but it doesn't look hipster enough to belong in the React world

Haha! Well done :). There are actually a bunch of different ways to do this - I was just having this conversation with several people this past week. We do talk about this WAY later in the tutorial - see https://symfonycasts.com/screencast/reactjs/server-pass-props and the chapter before it (for a bit of the setup).

In that chapter, we simply set a global variable. But, other options include - setting data- attributes (like you've done) and creating a special &lt;script type="text/json" id="my-original-data"&gt; and then reading and decoding that JSON. That last one actually came out as the "subtle" winner from my talks this pas week... though they are all approximately the same as far as security goes, etc.

So, long way of saying - your way isn't so un-hipster actually ;). Though you can (and we show this in that other chapter) do some cool stuff where you use the ...spread operator so that you don't need to grab each piece of data and manually do the attribute={var} way of setting things.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^1.6", // 1.9.1
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.3
        "doctrine/doctrine-fixtures-bundle": "~3.0", // 3.0.2
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.1
        "doctrine/orm": "^2.5", // v2.7.2
        "friendsofsymfony/jsrouting-bundle": "^2.2", // 2.2.0
        "friendsofsymfony/user-bundle": "dev-master#4125505ba6eba82ddf944378a3d636081c06da0c", // dev-master
        "sensio/framework-extra-bundle": "^5.1", // v5.2.0
        "symfony/asset": "^4.0", // v4.1.4
        "symfony/console": "^4.0", // v4.1.4
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/form": "^4.0", // v4.1.4
        "symfony/framework-bundle": "^4.0", // v4.1.4
        "symfony/lts": "^4@dev", // dev-master
        "symfony/monolog-bundle": "^3.1", // v3.3.0
        "symfony/polyfill-apcu": "^1.0", // v1.9.0
        "symfony/serializer-pack": "^1.0", // v1.0.1
        "symfony/swiftmailer-bundle": "^3.1", // v3.2.3
        "symfony/twig-bundle": "^4.0", // v4.1.4
        "symfony/validator": "^4.0", // v4.1.4
        "symfony/yaml": "^4.0", // v4.1.4
        "twig/twig": "2.10.*" // v2.10.0
    },
    "require-dev": {
        "symfony/debug-pack": "^1.0", // v1.0.6
        "symfony/dotenv": "^4.0", // v4.1.4
        "symfony/maker-bundle": "^1.5", // v1.5.0
        "symfony/phpunit-bridge": "^4.0", // v4.1.4
        "symfony/web-server-bundle": "^4.0" // v4.1.4
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "dependencies": {
        "@babel/plugin-proposal-object-rest-spread": "^7.12.1" // 7.12.1
    },
    "devDependencies": {
        "@babel/preset-react": "^7.0.0", // 7.12.5
        "@symfony/webpack-encore": "^0.26.0", // 0.26.0
        "babel-plugin-transform-object-rest-spread": "^6.26.0", // 6.26.0
        "babel-plugin-transform-react-remove-prop-types": "^0.4.13", // 0.4.13
        "bootstrap": "3", // 3.3.7
        "copy-webpack-plugin": "^4.4.1", // 4.5.1
        "core-js": "2", // 1.2.7
        "eslint": "^4.19.1", // 4.19.1
        "eslint-plugin-react": "^7.8.2", // 7.8.2
        "font-awesome": "4", // 4.7.0
        "jquery": "^3.3.1", // 3.3.1
        "promise-polyfill": "^8.0.0", // 8.0.0
        "prop-types": "^15.6.1", // 15.6.1
        "react": "^16.3.2", // 16.4.0
        "react-dom": "^16.3.2", // 16.4.0
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^7.0.0", // 7.3.1
        "sweetalert2": "^7.11.0", // 7.22.0
        "uuid": "^3.2.1", // 3.4.0
        "webpack-notifier": "^1.5.1", // 1.6.0
        "whatwg-fetch": "^2.0.4" // 2.0.4
    }
}
userVoice