Learning React - Week 4

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.

CSS Animation

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:

npm scripts

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:

CSSTransitionGroup

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:

Temporary classes rendered by the CSSTransitionGroup

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:

Styles

Note: the lack of {}, : or ; in the .styl documents

This renders out animations that look great:

Animations first step

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:

Count code

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.

Count CSSTransitionGroup in Action

Now all that is left is to add the animation styles in:

Count Animation styles

To Do:
  • Import CssTransitionGroup from 'react-addons-css-transition-group' into Order.js
  • Modify the ul to be a CSSTransitionGroup & add the required attributes.
  • Modify the {count} to be wrapped in a CSSTransitionGroup & set the attribute key to {count} (so we have a unique reference available).
  • Add animation using CSS transitions in the _animations.styl document.
Today the app looks like:

App on Day 22 of the React for Beginners Course

[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:

App on Day 23 of the React for Beginners Course

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:

  1. Create a Facebook App with Facebook Login.
  2. Create a `Sign-in Method` with Facebook enabled on Firebase.
  3. Copy the `OAuth redirection URI` from the Firebase tab into the Facebook App settings.
  4. Enable `Embedded Browser OAuth Login` in the Facebook App settings. Save the changes.
  5. 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.
  • Set the default state of the uid & owner to null (in the Constructor method).
  • Create & bind an authenticate method that takes in provider. Import base (to connect to Firebase). Use the base.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 from App to Inventory 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 the authHandler method.
  • Hook up the Log Out button.
  • Increase the security of the database rules in Firebase.
Today the app looks like:

App on Day 24 of the React for Beginners Course

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.

Terminal output after running npm run build

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 in package.json
  • Modify react-router (in index.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 as 404.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.