React App with Firebase, AWS S3

2018-01-18

Start with an REACT APP

Follow the facebook official react page:

https://github.com/facebookincubator/create-react-app

import css File to the App

First you need to do

1
npm run eject

In some cases you will face an error which is

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HanslendeMacBook-Pro:test1 Hanslen$ npm run eject
> test@0.1.0 eject /Users/Hanslen/Desktop/test/test1
> react-scripts eject
? Are you sure you want to eject? This action is permanent. Yes
This git repository has untracked files or uncommitted changes:
GithubPages
Remove untracked files, stash or commit any changes, and try again.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! test@0.1.0 eject: `react-scripts eject`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the test@0.1.0 eject script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/Hanslen/.npm/_logs/2018-01-19T04_48_22_096Z-debug.log

The easier way to fix this problem is to move GithubPages(or the folder in your error) to another place and then run

1
2
git add .
git commit -m 'save before eject'

Then try npm run eject again. That should work. :D

After ejecting, we need to mofidy two files.

  1. ./config/webpack.config.dev.js

    search for .css, and find the following block:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    test: /\.css$/,
    use: [
    require.resolve('style-loader'),
    {
    loader: require.resolve('css-loader'),
    options: {
    importLoaders: 1,
    },
    },

    modify it to

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    test: /\.css$/,
    use: [
    require.resolve('style-loader'),
    {
    loader: require.resolve('css-loader'),
    options: {
    importLoaders: 1,
    modules: true,
    localIdentName: '[name]__[local]__[hash:base64:5]'
    },
    },

  2. ./config/webpack.config.prod.js

    still search for .css, and find:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    test: /\.css$/,
    loader: ExtractTextPlugin.extract(
    Object.assign(
    {
    fallback: {
    loader: require.resolve('style-loader'),
    options: {
    hmr: false,
    },
    },
    use: [
    {
    loader: require.resolve('css-loader'),
    options: {
    importLoaders: 1,
    minimize: true,
    sourceMap: shouldUseSourceMap,
    },
    },

    and modify use to

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    use: [
    {
    loader: require.resolve('css-loader'),
    options: {
    importLoaders: 1,
    minimize: true,
    sourceMap: shouldUseSourceMap,
    modules: true,
    localIdentName: '[name]__[local]__[hash:base64:5]'
    },
    },

    The in the js file, you can use:

    1
    2
    3
    4
    import classes from './test.css';
    //jsx, for the div
    <div className={classes.test}></div>

React Routing Tips

https://react-guide.github.io/react-router-cn/

You need to install two modules first.

1
2
npm install --save react-router //sometimes don't need
npm install --save react-router-dom

Writing a link:

1
2
3
4
import { Route } from 'react-router-dom';
//jsx part
<Route path='/:id' exact component={FullPost}></Route>

JS:

1
console.log(this.props.match.params.id);

Some other useful components of react-router-dom are:

NavLink, Link, withRouter, Switch(Load the first matching, and order is important), Redirect
1
<Redirect from="/all-courses" to="/courses" />

Query Params:

You can pass them easily like this:

<Link to="/my-path?start=5">Go to Start</Link>

or

1
2
3
4
5
6
<Link
to={
pathname: '/my-path',
search: '?start=5'
}
>Go to Start</Link>

React router makes it easy to get access to the search string: props.location.search .

But that will only give you something like ?start=5

You probably want to get the key-value pair, without the ? and the = . Here’s a snippet which allows you to easily extract that information:

1
2
3
4
5
6
componentDidMount() {
const query = new URLSearchParams(this.props.location.search);
for (let param of query.entries()) {
console.log(param); // yields ['start', '5']
}
}

URLSearchParams is a built-in object, shipping with vanilla JavaScript. It returns an object, which exposes the entries() method. entries() returns an Iterator - basically a construct which can be used in a for...of... loop (as shown above).

When looping through query.entries() , you get arrays where the first element is the key name (e.g. start ) and the second element is the assigned value (e.g. 5 ).

Fragment:

You can pass it easily like this:

<Link to="/my-path#start-position">Go to Start</Link>

or

1
2
3
4
5
6
7
<Link
to={
pathname: '/my-path',
hash: 'start-position',
search: '?title='+course.title //it can be object
}
>Go to Start</Link>

React router makes it easy to extract the fragment. You can simply access props.location.hash .

1
2
this.props.history.push({pathname: '/' + id});
this.props.history.push('/'+id); //also works

Handling the 404 Error

Under all route components, write:

1
<Route render={() => <h1>Not found</h1>}/>

You can also use the component instead of render as well.

Lazy loading

Adding /Hoc/asyncComponent/asyncComponent.js, and write the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, {Component} from 'react';
const asyncComponent = (importComponent) => {
return class extends Component{
state = {
component: null
}
componentDidMount(){
importComponent()
.then(cmp => {
this.setState({component: cmp.default});
});
render(){
const C = this.state.component;
return C ? <C {...this.props} />:null;
}
}
}
}
export default asyncComponent;

How to use this file?

1
2
3
4
5
6
7
8
9
import asyncComponent from '../hoc/asyncComponent;
//this is a valid component
const AsyncNewPost = asyncComponent(() => {
return import('./NewPost/NewPost');
});
//Modify the <Route /> to
<Route path="/new-post" component={AsyncNewPost}/>

Using Axios for sending requests

Follow the Axios official page:

https://github.com/axios/axios

Passing Arguments via Query Params

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const queryParams = [];
for(let i in this.state.ingredients){
queryParams.push(encodeURIComponent(i)+'='+encodeURIComponent(this.state.ingredients[i]));
}
const queryString = queryParams.join('&');
this.props.history.push({
pathname: '/checkout',
search: '?'+queryString
});
//for that target page:
componentDidMount(){
const query = new URLSearchParams(this.props.location.search);
const ingredients = {};
for(let param of query.entries()){
ingredients[param[0]] = +param[1];
}
this.setState({ingredients: ingredients});
}

Axios Common REST tips

For getting some data from firebase:

1
2
3
4
5
6
7
axios.get(yourURL+'.json')
.then(res => {
//do something
})
.catch(err => {
//do something
});

For posting some data to firebase:

1
2
3
4
5
axios.get(yourURL+'.json').then(res => {
//do something
}).catch(err => {
//do something
});

For updating some data to firebase:

1
2
3
4
5
6
axios.patch(yourURL+'.json', yourObject)
.then(res => {
//do something
}).catch(err => {
//do something
});

Deploy the REACT App to firebase Host

Firstly, you need to type the following command in the terminal:

1
firebase deploy

You will generate a firebase.json file, and sometimes, it is empty. So you need to add the following code into that file:

1
2
3
4
5
6
7
8
9
10
{
"hosting": {
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}

remember key “public” stands for the folder you will be deploying. Generally, when we run

1
npm run build

We will have a “build” folder. So we set “build” to the “public” key.

Deploy to AWS S3

Firstly, you need to install aws-cli. There are multiple ways to do that, but the easiest and efficient way I find is:

1
brew install awscli

Then configure your aswcli by typing:

1
aws configure

You can find your access key and secret access in MySecurity credentials, then expand Access keys (access key ID and secret access key) you will get that.

Then configure out aws s3:
Go to page: https://s3.console.aws.amazon.com/s3/home?region=us-east-1

Then click Create Bucket, enter Bucket name -> Set properties(as default) -> Set permission(Set manage public permissions as Grant public read access to this bucket)->Review(Click create bucket).

Configure that bucket

Click that bucket name,

then click Bucket Policy, copy the following testing to the Bukcet policy editor:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"Version": "2012-10-17",
"Id": "Policy1517586076687",
"Statement": [
{
"Sid": "Stmt1517586072794",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::testreact.com/*"
}
]
}
You may need to change Resource to your own…

An easier way to do it is to use http://awspolicygen.s3.amazonaws.com/policygen.html (AWS Policy Generator)

Then Save it.

Go to Properties section: Click (Enable)Static website hosting -> The EndPoint is your website URL. And the index document is your defualt index page.

Uploading files

Type:

1
aws s3 cp build s3://testreact.com --recursive

And remeber change the s3://testreact.com to s3://yourBucketName

Now you can access the Endpoint to view your React App!


Comments: