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).
Build the Static App First
Keep on Learning!
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 SubscribeOne of our big goals in this tutorial is to create a repeatable path to success. And, we're already doing that! Step 1 is always to create an entry file. That file doesn't do much except render a React
component onto your page.
Step 2, in that React component, build out an entirely static version of your app. First do this in pure HTML. Then, create some hardcoded variables and render those. For example, we first built one dummy tr
element by hand and then created a hardcoded repLogs
array and used that to build the rows.
So, step 2 for success is to build your entire UI statically... and then, soon, we will make things dynamic and fancy.
Adding the Static Form
The only UI that's missing form our app now is the form below the table. No problem! Go back into templates/lift/index.html.twig
. Ah, the form lives in another template: _form.html.twig
. Open that: it's just a normal, boring HTML form. That's perfect! Copy all of it, close the file, go back into RepLogApp
and, under the table, paste!
// ... lines 1 - 2 | |
export default class RepLogApp extends Component { | |
render() { | |
// ... lines 5 - 15 | |
return ( | |
<div className="col-md-7"> | |
// ... lines 18 - 48 | |
<form className="form-inline js-new-rep-log-form" noValidate | |
data-url="{{ path('rep_log_new') }}"> | |
// ... lines 51 - 83 | |
</form> | |
</div> | |
// ... line 86 | |
); | |
} | |
} |
Except, scroll up a little bit, because we need to do some cleanup! The form doesn't need this class anymore: that was used by the old JavaScript. The same is true for the data-url
element. And noValidate
disables HTML5 validation. But, HTML5 validation is nice to have: it will enforce the required
attribute on the fields. So, remove it.
// ... lines 1 - 15 | |
return ( | |
// ... lines 17 - 48 | |
<form className="form-inline"> | |
// ... lines 50 - 85 | |
); | |
// ... lines 87 - 89 |
Oh, but I want you to notice something! The attribute was noValidate
with a capital "V"! In the original template, it was novalidate
with a lowercase "v": that's how the property is called in HTML. When we pasted it, PhpStorm updated it for us. This is one of those uncommon situations - like class
and className
where the HTML attribute is slightly different than what you need to use in React. I want to point that out, but don't over-think it: almost everything is the same, and React will usually warn you if it's not.
Cool! Try it out: refresh! Awesome! We have a form!
Using form defaultValue
Hmm, but the styling is not quite right. And, we have a warning about using a defaultValue
. Let's fix that first. We'll talk a lot more about forms in React later. But basically, React is a control freak, it really really* wants to manage the values of any elements on your form, including which option is selected. So, instead of using selected="selected"
, you can use defaultValue=""
on the select
element and set it to the value of the option you want. I'll skip that part because the first option will be automatically selected anyways.
Adding Ugly Manual Whitespace
Ok, back to the styling problem. Inspect element on the form itself, right click on it, and go to "Edit as HTML". Ah, React renders as one big line with no spaces in it. 99% of the time... we don't care about this: usually whitespace is meaningless. But, in this case, the form is an inline element: we need a space between the first two fields, and between the last field and the button. Without the space, everything renders "smashed" together.
The fix is both simple... and ugly: use JavaScript to print an extra space. Do it in both places. Yep, weird, but honestly, I rarely need to do this: it's just not a problem you have very often.
// ... lines 1 - 48 | |
<form className="form-inline"> | |
<div className="form-group"> | |
// ... lines 51 - 66 | |
</div> | |
{' '} | |
<div className="form-group"> | |
// ... lines 70 - 77 | |
</div> | |
{' '} | |
// ... lines 80 - 82 | |
</form> | |
// ... lines 84 - 89 |
Try it again... yep! It looks much better! And we have our static app!
Using the Dev Server
Before we go kick more butt, I want to make one adjustment to our workflow. We're using Webpack Encore. We ran it with:
yarn run encore dev --watch
Thanks to that, each time we update a file, it notices, and re-builds our assets. But rather than using encore dev
, use encore dev-server
. This is pretty interesting: instead of writing physical files to our public/build
directory, this starts a new web server in the background that serves the built assets.
Check this out: go back and refresh the app. No visible differences. But now, view the page source. Suddenly, instead of pointing locally, like /build/layout.css
, every asset is pointing to that new server: http://localhost:8080
! This magic URL changing is thanks to Webpack Encore and some config changes we made to our Symfony app in the Encore tutorial.
The web server - http://localhost:8080
- is the server that was just started by Webpack. When you request an asset from it, Webpack returns the latest, built version of that file. What's weird is that the built assets are no longer physically written to the filesystem. Nope, we just fetch the dynamic version from the new server.
Ultimately... this is just a different, fancier way to make sure that our code is always using the latest version of the built assets. But, as your app gets more complex, it may become possible for you to refresh your page before Webpack has been able to physically write the new files! However, if you use the dev-server
and refresh too quickly, your browser will wait for the CSS or JavaScript files to be ready, before loading the page. And, as an added "nice thing", the dev server will cause our browser to automatically refresh whenever we make changes.
Anyways, or goal was to build our entire app with a static UI. And, we've done that! Sure, we have some fanciness: we learned that we can pass "props" into our components and then use those to render things dynamically. So, our app is "kind of" dynamic, because we can control different parts of how it looks by passing different props. But... once our component is rendered, it's static. For example, once we render RepLogApp
with a heart... it will have a heart forever.
But, the whole point of using React is so that our UI will automagically update when data changes! And we'll do that with something very, very important called state.
I think In th end of the tutorial we gonna delete all the twig files ? can we use a template engine with react?