If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThe RepLogApp
component eventually needs to render all the elements we see on the original app: with a table and a form. No problem! Go find the template for this page: templates/lift/index.html.twig
. Hey! There's our table! And the form lives in this included template. Copy the entire old markup. Then, go back to RepLogApp
and replace our code with this. But, I don't want to worry about the form yet... so remove that. Oh, and I kinda liked the withHeart
feature, so let's make sure we're still printing {heart}
.
... lines 1 - 2 | |
export default class RepLogApp extends Component { | |
render() { | |
... lines 5 - 9 | |
return ( | |
<div className="col-md-7 js-rep-log-table"> | |
<h2>Lift Stuff! {heart}</h2> | |
<table className="table table-striped"> | |
<thead> | |
<tr> | |
<th>What</th> | |
<th>How many times?</th> | |
<th>Weight</th> | |
<th> </th> | |
</tr> | |
</thead> | |
<tbody> | |
</tbody> | |
<tfoot> | |
<tr> | |
<td> </td> | |
<th>Total</th> | |
<th className="js-total-weight"></th> | |
<td> </td> | |
</tr> | |
</tfoot> | |
</table> | |
</div> | |
); | |
} | |
} |
Sweet! Once again, PhpStorm just did an amazing thing - automagically - when we pasted in the code. Check out the className="table table-striped"
. Hmm, look at the original code in the template: it had class=""
... because that's what it should be in HTML! Well... in React, you actually can't use class
. You can see it's highlighted:
Unknown property class found, use className instead
React can't use class
because class
is a keyword inside of JavaScript. And for that reason, in JSX, you need to use className
instead. But ultimately, this will render as a normal class
attribute on the page.
And, don't worry, there aren't tons of weird attributes like this in React: this is basically the only one you're likely to use.
The point is: PhpStorm is smart enough to convert our pasted class
props to className
automatically. Notice that I said props
: while we think of these as HTML attributes, they're technically props
, which React ultimately renders as attributes.
Let's clean a few things up! We don't need this js-rep-log-table
class: it was used by the old JavaScript. And below, this is the column that prints the total weight. Remove the class that was used by the old JavaScript and, for now, just put a TODO inside.
And finally, just to see how it looks with data, let's hack in one fake row full of invented stuff. Use ... for the last column: someday, we'll add a delete link here.
... lines 1 - 9 | |
return ( | |
<div className="col-md-7"> | |
... lines 12 - 13 | |
<table className="table table-striped"> | |
... lines 15 - 22 | |
<tbody> | |
<tr> | |
<td>Big Fat Cat</td> | |
<td>10</td> | |
<td>180</td> | |
<td>...</td> | |
</tr> | |
</tbody> | |
<tfoot> | |
<tr> | |
... lines 33 - 34 | |
<th>TODO</th> | |
... line 36 | |
</tr> | |
</tfoot> | |
</table> | |
</div> | |
... line 41 | |
); | |
... lines 43 - 45 |
Cool! Building a static version of your app first is a great way to start. And JSX makes that really easy.
Let's go check it out: find your browser and refresh! Hey hey! This is starting to look real!
In our old app, on page load, we make an AJAX request to load the "rep logs"- that's what we call our data - and use that to render the table.
Eventually, we'll do the same thing in React. But before you work with dynamic data, you should first make your app render using static data. Check this out, inside render()
, create a new constant called repLogs
and then set that to some fake data that matches the format of your API. We now have 3 fake rep logs with id
, itemLabel
and totalWeight
.
... lines 1 - 3 | |
render() { | |
... lines 5 - 9 | |
const repLogs = [ | |
{ id: 1, reps: 25, itemLabel: 'My Laptop', totalWeightLifted: 112.5 }, | |
{ id: 2, reps: 10, itemLabel: 'Big Fat Cat', totalWeightLifted: 180 }, | |
{ id: 8, reps: 4, itemLabel: 'Big Fat Cat', totalWeightLifted: 72 } | |
]; | |
... lines 15 - 54 | |
} | |
... lines 56 - 57 |
Below, inside the tbody
, we basically want to convert each "rep log" into a tr
React element with the data printed inside of it. To do that, we're going to use a really common pattern in React... which might feel a bit weird at first.
Above render()
, create a new constant called repLogElement
set to repLogs.map()
. Pass this a callback function with one argument: repLog
. I'll use the arrow syntax for the callback. Inside, we're going to return a React element via JSX: add parenthesis so we can use multiple lines. Then, just build out the row: <tr>
, then <td>
with {repLog.itemLabel}
.
If you're not familiar with the map
function, that's ok: it's much less common in PHP. Basically, it loops over each element in repLogs
, calls our function, and then, whatever our function returns, is added to the repLogElement
array. So, ultimately, repLogElement
will be an array of <tr>
React element objects.
Add the next <td>
. Let's see... ah, this column is "How Many". Fill in the second column with {repLog.reps}
, then another <td>
with {repLog.totalWeightLifted}
and finally one more with ...
: this will be the delete link... someday.
.
... lines 1 - 15 | |
const repLogElements = repLogs.map((repLog) => { | |
return ( | |
<tr key={repLog.id}> | |
<td>{repLog.itemLabel}</td> | |
<td>{repLog.reps}</td> | |
<td>{repLog.totalWeightLifted}</td> | |
<td>...</td> | |
</tr> | |
) | |
}); | |
... lines 26 - 57 |
Great! Wait... but the tr
has a little warning: something about a missing key
prop. We'll talk about that in a minute. Until then, ignore that silly warning! What could go wrong?!
Now that we have an array of React element objects, this is pretty sweet: go down, delete the hardcoded row and - wait for it - just print repLogElements
.
Yea, it looks a bit crazy: we're literally printing an array of React elements! But, try it - go back to your browser and refresh! It works! It prints each row!
But, we have a big warning from React. Let's fix that next, and introduce a new best practice to keep our code readable.
Hey Dmitriy
What you mean? Does your PHPStorm is not recognizing JSX syntax? If that's the case you have to enable it by going to Settings > Languages & Frameworks > Javascript - in there, you have to choose "React JSX", just that I'm not sure if you have to install a PHPstorm plugin first.
Cheers!
No, I would like that when inserting HTML code in my document (in "render" function), it would automatically be converted to the JSX, like this video.
For this document i enable Settings > Languages & Frameworks > Javascript > React JSX, but it does not work.
Which plugin can do this?
Ah, I got you. I would think that that should be enough but probably you need to install "eslint" as well. Check this video to know how to install & configure it: https://symfonycasts.com/sc...
// 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
}
}
// 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
}
}
Unfortunately, my PHPStorm does not automatically convert HTML to JSX. How can I set it up?