This article originally appeared on stef.ninja.
This is the final post in my blogging driven learning exploration of React. I have been working through the React for Beginners course from Wes Bos and documenting my takeaways from each lesson.
This is an AWESOME course and I highly recommend it to anyone looking to learn React.
You can find more notes in Week 1, Week 2 and Week 3 of the series.
Day 22: Animating React Components
Animations are a fun way to inject movement into our webpage. This can be done via the traditional method of css (like the GIF below) or some more interactive React animations that we will explore further.
NPM Scripts & CSS Preprocessors
Today's lesson touched on a few new concepts including the use of styl as a CSS preprocessor. We also explored how this can be integrated in the npm scripts to enable hot reloading during development.
Using the npm scripts in the package.json
we are able to call $ npm run watch
in the terminal. It will then concurrently run start
& styles:watch
. Then if one of them breaks it's going to kill the other one and show an error in the terminal. This makes development really easy.
This is what the npm scripts look like at this point:
Note: In our development environment at work we don't use external CSS or CSS preprocessors as we have opted for styled components (I talked more about this in Day 4).
Animations
Now that we have our styling options sorted out we can get into the meat of animating React Components.
Our aim today is to animate our order with some subtle movements:
- When a new fish is added to the order it should animate in.
- When a fish is removed from an order is should animate out.
- When the number of lbs of a fish increases the number should animate up.
These are only small movements and we will do a lot of work to make them happen but the polished finish that they give the website is a nice touch.
The first step we need to do is open up our HTML to include classes that we can hook into for our animations. You can do this using the CSSTransitionGroup.
We modified the ul
component to be a CSSTransitionGroup
component instead. This looks like this in the code:
Note: By adding the component="ul"
this tells React that we want the component to render a unordered list component in HTML but maintain it as a ReactCSSTransitionGroup in React.
The additional attributes on the CSSTransitionGroup
component control temporary classes on the ul
HTML component. You can see how these classes display via the developer tools:
These temporary classes can then be hooked into via CSS. We are able to use CSS Transitions because there is an initial state (class order-enter
/order-leave
) and a final state (class order-enter-active
/order-leave-active
) that we can reference.
These can then be styled like this:
Note: the lack of {}, : or ;
in the .styl
documents
This renders out animations that look great:
The final task in todays lesson is to animate the count of the lbs for each fish in the order. The animation that we are aiming for looks like the previous number is being pushed out by the new number. This requires a duplication of the element that will let us animate the 'old' version of the element out whilst simultaniously animating the 'new' version of the element in.
This animation style requires us to duplicate the {count}
span. To do this we will add a key
attribute with the value {count}
as well as wrap the {count}
in a CSSTransitionGroup
and additional span
. The code looks like this:
This results in two spans being created - a new one with an updated {count}
and a order-enter-active
class and the original {count}
that now has a order-leave-active
class on it.
Now all that is left is to add the animation styles in:
To Do:
- Import CssTransitionGroup from 'react-addons-css-transition-group' into
Order.js
- Modify the
ul
to be aCSSTransitionGroup
& add the required attributes. - Modify the
{count}
to be wrapped in aCSSTransitionGroup
& set the attributekey
to{count}
(so we have a unique reference available). - Add animation using CSS transitions in the
_animations.styl
document.
Today the app looks like:
[Video 22: Animating React Components]
Day 23: PropTypes for the Win
What are PropTypes? They are validators that make sure the data coming into a component is valid.
Today's lesson covered inserting PropType validations into every component of the app. We used the React.PropType
to do this which you can read more about here.
As of React v15.5 we should be using the prop-types
library instead of the React.PropTypes
. This moves away from accessing PropTypes
from the main React
object which seems to be a common move in React v15.5. Read more about migrating from React.PropTypes here.
To Do:
- Add proptype validators to all components.
Today the app looks like:
Note: There is no visual changes that were made in today's lesson.
[Video 23: Component Validation with PropTypes]
Day 24: Authentication with Firebase
Currently the database and state can be edited by all visitors. This level of authentication and security is too low for a production level app so we really need to reign that back in.
To do this we will create an application that requires you to login with GitHub, Facebook or Twitter before you can edit the Inventory of a store. The first person to login to that store will be saved as the owner of that store (not a super practical realworld workflow but good enough for todays purposes). This authentication is all going to be completed client side with the backend being handled by Firebase.
All of the sensitive API Secrets and Client IDs are going directly into Firebase rather than our client facing app. This adds a level of security that we don't even need to think about.
Login Authentication with Firebase
Wes makes this look super simple. He follows these steps:
- Create a Facebook App with Facebook Login.
- Create a `Sign-in Method` with Facebook enabled on Firebase.
- Copy the `OAuth redirection URI` from the Firebase tab into the Facebook App settings.
- Enable `Embedded Browser OAuth Login` in the Facebook App settings. Save the changes.
- Copy the `App ID` and `App Secret` into the Firebase Facebook authentication settings.
You can do a similar process for all of the Sign-in Providers (Email/Password, Google, Facebook, Twitter, GitHub).
Connecting to Firebase
Connecting to Firebase is super simple with the Rebase package that we imported. We simply need to Import base from '../base';
at the top of our component and we can now connect to Firebase.
It appears that this lesson covers an outdated method of connecting to Firebase.com. The new Firebase console and 3.x SDKs ask for a diferent method. I will explore connecting to Firebase.com using the method that Wes uses and then also the new and updated method. I will need to research how 'Rebase' works with the new vs old method. You can read more about Upgrading your Web / Node.js app from Firebase.com.
The Firebase.com method of Authentication via OAuth is:
ref.authWithOAuthPopup("twitter", function(error, authData) {
if (error) {
// An error occurred
console.error(error);
} else {
// User signed in!
var uid = authData.uid;
}
});
This can be compared to the new Firebase Authentication functionality that lives behind the firebase.auth()
service. There have been heaps of methods that have been renamed in this transition and the way that you authenticate users with OAuth is one of those:
var auth = firebase.auth();
var provider = new firebase.auth.TwitterAuthProvider();
auth.signInWithPopup(provider).then(function(result) {
// User signed in!
var uid = result.user.uid;
}).catch(function(error) {
// An error occurred
});
Securing Firebase
Earlier in our lessons we opened the security rules of our Firebase database right up. This was to help with ease of development. Now that we are nearing production release we need to close these right down. Wes provided sample rules for this in security-rules.json
:
These rules don't allow people who know the syntax of Firebase to edit it in a destructive manner. So the first rule doesn't allow anyone to delete currently existing data but allows all data to be read. The second rule only allows the store owner to edit data that already exists.
{
"rules": {
// won't let people delete an existing room
".write": "!data.exists()",
".read": true,
"$room" : {
// only the store owner can edit the data
".write" : "auth != null && (!data.exists() || data.child('owner').val() === auth.uid)",
".read" : true
}
}
}
To Do:
- Create GitHub, Twitter & Facebook sign up methods in the
Auth
section of Firebase. - Create a method to render out the login buttons in the
Inventory
component. - Put a conditional statement into the
Inventory
render method.- Check if the user is logged in. If not, return the
renderLogin
method. - Check if they're the owner of the current store. If not, return a 'Sorry you're not the owner' & 'Logout' button.
- If they pass both of these checks the user must be logged in & the owner of the current store. So, we will leave the default return in place.
- Check if the user is logged in. If not, return the
- Set the default state of the
uid
&owner
tonull
(in the Constructor method). - Create & bind an
authenticate
method that takes inprovider
. Import base (to connect to Firebase). Use thebase.AuthWithOAuthPopup
method. - Create & bind an
authHandler
method that handles errors & returns the User information payload from Firebase. Store the User Information in the state. - Pass the
storeID
down fromApp
toInventory
via props. - Grab the store information from Firebase.
- Check whether the store has an owner set, if not, set the current user as the owner.
- Hook into the
componentDidMount
lifecycle method and check to see if there is an authenticated user present. If so, call theauthHandler
method. - Hook up the
Log Out
button. - Increase the security of the database rules in Firebase.
Today the app looks like:
This will be the last image I put up detailing the App progress as the final lessons are around deployment (with no visual changes).
[Video 24: Authentication]
Day 25: Building React for Production
create-react-app
has a build step. It is as simple as running the command npm run build
in the terminal. This process exports all of the files that we will need to run our app in production. This includes minifying the JS and CSS files and creating source-maps for our JS and CSS. These source-maps will make debugging our app 100% easier by referencing the JS or CSS file that the code was written in rather than the compiled line.
It also outlines how to upload and run this on both a local and a remote server.
The create-react-app
output at the top level is index.html
and a static
folder. This output doesn't come with an actual server. This means that if we just uploaded these files onto a server we would be served a bunch of errors. We need a server that knows to serve the index.html
regardless of the URL and that relinquishes the routing to the browser.
The next few lessons will be covering how to deploy this app onto various servers that fit this bill. As these are all related to building React for Production I will complete them all today.
Deploying to now.sh
now.sh publishes your JavaScript (Node.js) or docker powered apps, websites and services in the cloud easily, quickly and reliably. It also comes with free open source hosting. This means deployment of anything you're happy to open source is quick, free (up to a limit) and easy.
The now-serve
(found here) command that Wes uses is depreciated and instead we are directed to use now-static
(found here). I was unable to get this working today and will revisit it after learning the other deployment processes.
To Do:
- Run
npm run build
command. - Choose a server to deploy to.
- Deploy to a server.
[Video 25 & 26: Building React for Production, Deploying to now.sh]
Day 26: Deploying to GitHub Pages
Another (hacky) way that we can host our create-react-app
application is by using GitHub Pages.
This process is 'hacky' because we need to work around the fact that our Application will be hosted in a subfolder (rather than the root directory). To account for this before running the build process we need to add "homepage": "https://YOURGITHUBNAME.github.io/YOURREPONAME"
. We will also need to alter the React Router to account for this subfolder.
GitHub pages doesn't allow you to control the routing for your pages so the suggested (hacky) way around this is to duplicate the index.html
page and rename the copy as 404.html
. This will mean that everytime the browser receives a 404 code the app is still presented. I hate this as a concept and am immediately taking this page down & deleting this repo.
To Do:
- Set up a blank GitHub repo.
- Set
homepage
inpackage.json
- Modify
react-router
(inindex.js
) to run in a subfolder. (set the attribute 'basename' equal to the subfolder name). You can do this by getting the variable from the URL with:
const repo = `/${window.location.pathname.split('/')[1]}`;
const Root = ()
...
<BrowserRouter basename={repo} >
- run
npm run build
- From the new build directory initialise a new Git Repo (with
git init
). - Add the remote (with
git remote add origin git@github.com:YOURGITHUBNAME/YOURREPONAME.git
) - Run
git add -A
,git commit -m "fishy"
,git push -u origin master
. - In the settings of your GitHub repo publish the
master branch
as the source for your GitHub page. - Duplicate
index.html
as404.html
& commit & push this change.
[Video 27: Deploying to GitHub Pages]
Day 27: Deploying to an Apache Server
Now we're getting into the good stuff. Today we're deploying on a standard Apache server (like the kind you pick up from GoDaddy or Bluehost for a couple of bucks a month).
If you're going to deploy to a subdirectory (not the root domain or a subdomain) then you will need to update the react-router
information in index.js
and the hompage:
declaration in package.json
. If not, these additions can be deleted.
We need to tell the server that index.html
should be served for ALL routes. For an Apache server you can do this with a .htaccess
file. Enter the following:
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME}% !-f
RewriteCond %{REQUEST_FILENAME}% !-d
RewriteRule . /index.html [L]
These rules breakdown to - whenever you have ANY /
serve up either the requested filename OR if nothing is found then serve up index.html
instead.
Note: To do this for an nginx
server you could use the following in an nginx.conf
file:
location / {
try_files $uri /index.html;
}
To Do:
- run
npm run build
- FTP these files up to your Apache server.
- Create a
.htaccess
file. - Allow the domain on Firebase (so your login OAuth continues to work)
[Video 28: Deploying to an Apache Server]
Day 28: Property Initializers and Getting Rid of .bind()
Anytime we want to add a custom method that we want bound to the instance of our component we need to use .bind()
in the constructor method. This is a lot of repeated and convoluted code and in today's lesson Wes shows us how to get rid of it using public class fields that are proposed for future iterations of JavaScript (ES6).
Note: Wes references the ES Class Fields & Static Properties proposal to JavaScript but this has since been merged with another proposal to form the ESnext class features for JavaScript.
So how does these Property Initializers relate to removing .bind()
?
We can remove this from the constructor()
:
this.loadSamples = this.loadSamples.bind(this)
We then alter the method from:
loadSamples() {
this.setState({
fishes: sampleFishes
});
}
TO:
loadSamples = () => {
this.setState({
fishes: sampleFishes
});
};
This works because the arrow function binds to the parent (essentially doing the same work as the .bind()
we were using earlier with much less hassle). It is important to note that this is a feature that hasn't been implemented into JavaScript yet and there is no guarantee that it ever will.
We are also able to remove any state declarations that were made within the constructor()
:
this.state = {
fishes: {},
order:{}
}
We can then remove the dot notation and set it outside of the constructor()
:
state = {
fishes: {},
order:{}
};
This is a property Initializer that has been added to every instance of App. This means that every instance of this component will be initialised with this state.
Futher dot notation removal can happen to anything that needs to happen after the component. For example, our propTypes are checked below the render()
of the component. These can be moved inside the component return and given a static
flag (as we don't need a new version everytime there is a new instance of this component).
App.propTypes = {
params: React.PropTypes.object.isRequired,
}
This can be moved into the render()
function and modified to:
static propTypes = {
params: React.PropTypes.object.isRequired,
};
To Do:
- Replace
.bind()
Property Initializers. - Move all
propTypes
into the component & make them static.
[Video 29: Future React Today - Property Initializers and getting rid of .bind()]
Day 29: Throwing Away the create-react-app Parachute
create-react-app
is brilliant because it handles all of the hard behind the scenes magic (cough webpack cough) and allows you to just get in there and start creating. However, there may be a time where the default set up isn't going to work for you any more and you need to eject from create-react-app
.
Warning: There is no way to 'uneject' from create-react-app so make sure you are doing this on a GitHub branch.
Ejecting from create-react-app
is as simple as runinng the command npm run eject
.
It will then install all of the dependencies that the app needs to work. It will also create two new directories (config & scripts). It will also add a literal butt-tonne of devDependencies
to your package.json
file.
You can now edit and update the webpack config, eslintConfig and babel as needed.
To Do:
- Create new
ejected
branch in repo - Run command
npm run eject
- Customise as needed.
[Video 30: Ejecting from create-react-app]
Day 30: Deploying to Firebase
It's the last day of my 30 day challenge and I have run out of official React for Beginners
lessons. The one thing that I was curious about during the last couple of lessons is how we could use Firebase to host the app (as well as handle the persistent state management with the database). I'm also curious if we would be given enough control over the routing to server index.html
regardless of the location.
With a little bit of quick research I can see that this is not only possible but it looks as though Firebase is built for it. So without further ado, let's get this app deployed onto Firebase hosting.
Deploying create-react-app to Firebase
The create-react-app
documentation goes through how to deploy your app to Firebase. Follow the instructions here (as they will be better than anything I can describe).
Customising Hosting Behaviour on Firebase
Firebase opens up the control of how content is hosted - this includes custom error pages, redirects, rewrites and headers. In our case, we're interested in rewrites.
To do this I added some customisations to the firebase.json
document. My additions tell Firebase to make build
the public directory and redirect all routes to the index.html
file. It also tells it to ignore the files that don't need to be included in deployment.
It now reads like:
{
"hosting": {
"public": "build",
"rewrites": [{
"source": "**",
"destination": "/index.html"
}],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
Now you can view my App here if you ever want to check it out.
To Do:
- Install Firebase CLI.
- Run the
create-react-app
build command. - Customise the
firebase.json
- Deploy to Firebase.
Read more in this series: Week 1, Week 2, and Week 3 Or get the React for Beginners Course for yourself.
Top comments (2)
could you send me your skype i would like ask some thing
Hi there,
I don't use Skype. You can contact me via twitter; twitter.com/stefdotninja