The README.md
gives you adequate information on how to clone 7 Eleven CliQQ files, install dependencies and launch the example app. Once you have been successful at that, you might be interested in how things work under the hood. You might have come across this while reading README.md
-
Here's a curated list of packages (in no particular order) that you should have in-depth knowledge of, before starting with this project. However, the best way to have a complete list of dependencies is to see package.json.
There are 80+ node packages in package.json
. If you're willing to go that extra mile, we'd suggest you do a google on each and every package and find out how it works and fits in this boilerplate. That will give you a sense of how even the smallest of things work.
Note that several features are optional and there are instructions in the docs on how to remove them.
redux-saga
or reselect
- Offline-first, add to homescreen, performant web font loading and image optimization hereLet's start with understanding why this project has chosen this particular structure.
app
folder.internals
folder.mocks
are maintained autmatically by Jest.server
folder contains development and production server configuration files.app/
We use container/component architecture. containers/
contains React components which are connected to the redux store. components/
contains dumb React components which depend on containers for data. Generally, you'd want to treat one webpage (such as Login, Home etc.) as one container and a small part (such as Login form, Navigation bar) of that page as a component. But there are no rigid rules. You can bend this structure to your needs.
internals/
You can call this area the build engine of your app/
. Your source code cannot be executed as-is in the web browser. It needs to pass through webpack to get converted into a form that web browsers understand.
internals/webpack
: You'll most probably use EcmaScript 6 or EcmaScript 7 to write the source code of your app. EcmaScript is the standard for JavaScript. Most people are still using browsers which understand EcmaScript 5. So your code must be transpiled into browser-understandable code. To apply the transpiler to your source code, you will use webpack. Feeling the jitters already? Don't worry. Take a tea-break and then read on.
internals/generators
: This folder has the code to scaffold out new components, containers and routes. Read more about scaffolding in the docs.
There are other folders there too, but for brevity let's just skip them.
server
As the name suggests, this folder contains development and production server configuration.
These days when musicians produce music, they record different parts of the song separately. So vocals, drums, guitar, bass may be played in separate sessions and when they're satisfied with their work, the sessions are combined into a beautiful song. In a similar fashion, let's understand the role of different technologies and in the end, we'll see how everything converges into a single application.
You can launch the example app by running npm start
. To fully understand its workings, you'll have to understand multiple technologies and how they interact. From this point, we're going into an overdrive of implementation details. We'll simplify the technical jargon as much as we can. So please bear with us.
Your single page application hooks into app/index.html
. That means React will render your components inside a div
which is injected into index.html
.
But how do we include all of our react components into a single html? That's where webpack comes into the picture. Webpack will literally pack your application into small javascript chunks. These files will be included in a <script>
tag which is injected into index.html
.
When your application is deployed on a server, browsers will load this html file. The javascript files that webpack has included will be executed by the browser, thereby booting up your react application! It's magic really!! No, not really, though is certainly can seem that way. Let's dissect this phenomenon to better know what's really going on.
app/app.js
:When you run npm start
, a webpack development server will be launched inside your terminal. You can then start browsing at http://localhost:3000.
Before launching the server, Webpack requires an entry point to your application. Think of it as a door to your source code. In this boilerplate app/app.js
will be that entry point. Webpack will access the entire app from this file, transpile the application into ES5 and will create small chunks of transpiled code. Only the required chunks will be loaded in the browser so that you won't have to worry about the size of your application.
app/app.js
could be one of the most confusing files for any beginner. It contains all the things that are globally applied to your app. You'll see that there's a lot going on. Let's break it down.
babel-polyfill
This will emulate a full ES2015 environment which in turn enables cool stuff like generator functions, Promise
s, etc.store
is instantiated.history
object is created which remembers all the browsing history of your app. (BTW, very useful for analytics)routes.js
ReactDOM.render()
not only renders the root react component called <App />
, of your application, but it renders it with <Provider />
, <LanguageProvider />
and <Router />
.<Provider />
connects your app with the redux store
.<LanguageProvider />
provides language translation support to your app.<Router />
will have information for your application routes.<Router />
has the crucial information for your routes. Check out routes.js
to see how route path
s are mapped with application containers.
"/"
corresponds to container <HomePage />
"/features"
corresponds to container <FeaturePage />
"*"
i.e. all other paths correspond to the <NotFoundPage />
These containers along with their corresponding reducer and sagas are loaded asynchronously with the help of dynamic import()
. Whenever webpack encounters import()
in the code, it creates a separate chunk for those imports. That means for every route, there will be a separate chunk. And by corollary, only those javascript chunks will be downloaded by the browser which are required for the current route. So when you navigate to "/"
, only chunks related to <HomePage />
will be downloaded and subsequently executed. This makes your application incredibly lightweight and lightning fast.
Redux is going to play a huge role in your application. If you're new to redux, we'd strongly suggest you to complete this checklist and then come back-
Redux store
is the heart of your application. Check out store.js
to see how we have configured the store.
The store is created with the createStore()
factory, which accepts three parameters.
redux-logger
as a middleware, it will listen to all the actions being dispatched to the store and print previous and next state in the javascript console. It's particulary helpful for developers to track application activity.In our application we are using two such middleware.
store
.Reselect is a library used for slicing your redux state and providing only the relevant sub-tree to a react component. It has three key features-
Imagine an application that shows a list of users. Its redux state tree stores an array of usernames with signatures -
{id: int, username: string, gender: string, age: number }
.
Let's see how the three features of reselect help.
createSelector()
If your application is going to interact with some back-end application for data, we recommend using redux saga for side effect management. Too much of jargon? Let's simplify.
Imagine that your application is fetching data in json format from a back-end. For every API call, ideally you should define at least three kinds of action creators:
API_REQUEST
: Upon dispatching this, your application should show a spinner to let the user know that something's happening.API_SUCCESS
: Upon dispatching this, your application should show the data to the user.API_FAILURE
: Upon dispatching this, your application should show an error message to the user.And this is only for one API call. In a real-world scenario, one page of your application could be making tens of API calls. How do we manage all of them effectively? This essentially boils down to controlling the flow of your application. What if there was a background process that handles multiple actions simultaneously, communicates with redux store and react containers at the same time? This is where redux-saga comes into the picture. Redux-saga docs are so well-written that we could not put it better than this-
The mental model is that a saga is like a separate thread in your application that's solely responsible for side effects. redux-saga is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well.
The react-boilerplate building blocks interoperate to produce a seamless application. Let's join these pieces together.
<HomePage />
Run npm start
to launch the application. If you start browsing at https://localhost:3000, by default you will be navigated to the home page. Here, notice that route is "/"
, so the <HomePage />
container will be mounted. It is responsible for rendering a <form />
with a textbox and a <List />
of repositories.
mapDispatchToProps()
: Generally, we provide outgoing action creators (functions that create action objects) to the react component through this method. Notice that for every keypress in textbox, your state will be updated by dispatching a changeUsername
action to the store. So at any point in time, your redux state will hold the currently typed username. When you submit the form, another action, loadRepos
will be dispatched.
mapStateToProps()
: Generally, we provide incoming state from redux store to the react component through this method. Notice that the we do not provide the entire state to the component, simply because we don't want the react component to have access to irrelevant data. The state will be filtered by selectors such as selectRepos
, selectUsername
etc.
Together these two methods work like magic. When you type something in the textbox the following things will happen in a sequential manner-
changeUsername()
will send text to the redux store. The text can be accessed using evt.target.value
. Here, evt
is the onChange
event emmited by pressing a key.mapStateToProps()
will be triggered and your react component will get the new data.value
to your <Input />
.So you see, if you type something in the textbox, it will not be directly reflected in the DOM. It must pass through redux. Redux will update the state and return it to the component. It's the component's responsibility to show the updated data.
HomePage/sagas.js
You must be wondering where does the list of repositories come from! Sagas are primarily used for making API calls. Sagas intercept actions dispatched to the redux store. That means a saga will listen to the actions and if it finds an action of interest, it will do something.
Sagas are nothing but ES6 generator functions. These functions act as normal functions, the only difference is that they can be "paused" and "resumed" at any point in time. Redux saga provides an intuitive API for managing asynchronous operations.
Check out ProfilePage/sagas.js. It can be confusing for untrained eyes. But the API of redux-saga is self-descriptive.
fork
a saga to send it to the background. That way, your code will not get blocked even when the saga is continuously running.takeLatest
is used for listening for a particular action. In this case, it will wait for a LOAD_REPOS
action. Whenever you disptach this action, the saga will understand that you want to fetch repos from github's public API by calling getRepos()
.reposLoaded()
action will be dispatched which carries the data. When redux store receives this action, a reducer will set incoming data in the new state tree.An update has occurred! mapStateToProps()
will be triggered. <HomePage />
will receive the new data. It's now the container's responsibility to properly display the list of repositories.