There are lots of great resources on React out there. What might be missing is some projects mixing real world constraints like:
- API calls
- using external libraries (UI kits, http clients ...)
- some routing and global state management
- code quality good practices (linting, testing, git hooks, cis ...)
- automation / dev pipeline
- and more ...
The hard part is often to be able to put all those together. This is the goal of this project: provide a well-documented example of a front-end app with real-world features and constraints.
📔 Read the blog post where I explain why I made the project.
📺 Watch the video of the talk 🇫🇷
🚀 Get started right away
"I'm a developer, I don't read docs, just give me the gist!"
git clone https://github.com/topheman/npm-registry-browser.git
cd npm-registry-browser
npm install
npm start
It just works ...!
However, you should take a look a the rest of the docs (you will find explanations and advanced uses).
- A simple boilerplate. It aims to be more than that: expose quality sample code that you could learn from, at a project level.
- The ultimate answer. There are things you would have done differently and it's ok. Other things will evolve with time.
This project was bootstrapped with create-react-app.
The whole README from create-react-app
is available here.
Here's a list of some of the technologies I'm using:
- React / React Router
- material-ui / Downshift
- Eslint (with eslint-config-airbnb) / Prettier
- Jest / Cypress.io - enzyme / react-testing-library
- Axios / nock
🗒 Read the notes I took along the way.
- Nodejs v8
- npm v5
git clone https://github.com/topheman/npm-registry-browser.git
cd npm-registry-browser
npm install
npm start
Checkout API proxy for development to understand how the API servers are served in development mode.
npm run build
Will build the a production version of the website in the build
folder.
Once you've built you're app, you can test the build on a local server with:
npm run serve
The following command will run both unit and e2e (cypress) tests.
npm test
Check out Error npm test on MacOs Sierra if you're experimenting some troubles.
You can choose to run them separately.
You'll find unit tests spread in the src
folder inside __tests__
folders in files named like *.spec.js
.
I'm using Jest as a test runner and enzyme / react-testing-library as testing utilies.
npm run test:unit
: single run of the unit testsnpm run test:unit:watch
: run the unit tests in watch mode
Check out Error npm test on MacOs Sierra if you're experimenting some troubles.
End-to-end testing is a technique used to test whether the flow of an application right from start to finish is behaving as expected. The purpose of performing end-to-end testing is to identify system dependencies and to ensure that the data integrity is maintained between various system components and systems.
The entire application is tested for critical functionalities such as communicating with the other systems, interfaces, database, network, and other applications.
I'm using cypress.io for the e2e tests. You will find them in cypress/integration.
npm run test:cypress
: single run the e2e tests. It will:- build the project and serve it on http://localhost:5000 (that way, your tests reflect exactly what the end user would see in production)
- run the tests in cypress/integration folder
- tear down once tests are passed (or failed)
npm run test:cypress:dev
: use this one when you're coding your tests. It will:- spin up a dev server on http://localhost:3000 (so, you don't have to
npm start
) - open the cypress client that will let you choose which tests you want to run
- spin up a dev server on http://localhost:3000 (so, you don't have to
npm run test:cypress:debug-build
: use this if your e2e tests only fail on a production bundle, to debug the tests with the production version of your app. It will:- build the project and serve it on http://localhost:5000
- open the cypress client that will let you choose which tests you want to run
When you add a spec file, reference it in cypress/integration/index.spec.js (specific to my cypress.json config, explained bellow).
Cypress@3.x.x upgrade
Since cypress@3.x.x
, each spec file runs in isolation. The tests took much longer to run.
To speed them up, I decided to run all the files in the same spec (tests don't rely on state of previous state - they could be ran in isolation if needed).
A message is output to explain how to run them in a regular way:
- remove cypress/integration/index.spec.js
- update cypress.json, remove the
testFiles
entry.
I use eslint to check the coding style, with the following presets:
- eslint-config-airbnb: An advanced set of eslint rules for JavaScript and React made by Airbnb
- eslint-config-prettier: Turns off all rules that are unnecessary or might conflict with Prettier.
- eslint-config-react-app: Shipping preset from create-react-app
More on eslint configuration.
The following command will run the linter on your code base. This task is ran at pre-commit to ensure code quality.
npm run lint
Prettier is a great tool to enforce a consistent style accross your code base (usefull when working in teams).
Here is how to integrate it with your editor.
Once it's done, when you'll save a file, it will reformat it.
The following command will let you format your code base. This task is ran at pre-commit.
npm run pretty
More on prettier.
The demo is hosted on github-pages. A simple way to publish your app is to use the gh-pages package that will create a gh-pages
orphan branch on which it will commit and push.
The following script will build then publish your app on your github pages:
npm run deploy
Each git push
triggers a test suite on travis. The following will be ran:
- linting
- unit tests
- end to end test with cypress
The end to end test sessions are recorded, you can check them here.
On each commit (or PR) pushed to master
, if the tests are passing, a version of the website will be automatically deployed from travis to a staging server: https://staging-npm-registry-browser.surge.sh/ (that way, your QA team will be able to test your latest stable features before you release them)
On each tag pushed to master
:
- a new version will be deployed to the production server: https://topheman.github.io/npm-registry-browser/
- a mocked version will be deployed to this server: https://mock-npm-registry-browser.surge.sh/ (read more about mocks)
- the artefacts generated during build (the
build
folder) will be uploaded to the related release, so that you could re-download them at any time (see releases section)
🚚 More infos about Continuous Deployment
To have uniform commit messages, I follow the AngularJS git commit guidelines, please take a look at it.
It also makes it easier to generate changelogs.
To generate changelog:
npm run generate-changelog -- v1.1.0 v1.2.0
Ready to be pasted to the github releases part.
You can test an online demo of the mocked version of the app (open the console to see the mocked requests).
Thanks to src/services/apis, the api calls can be mocked at any time. The following command will let the api manager serve the mocks saved in src/services/apis/mocks.
npm run dev:mock
It can be useful when the front-end and back-end teams are developing the same feature at the same time, so as a front-end developer, you don't have to wait for the server to be completed.
It can also be applied once you have a backend to mock your api calls while coding or testing, to have deterministic responses from your http client.
I made a utility based on nock in bin/record-http-mocks.js to automate the recording of the mocks by declaring which urls you want to mock and automatically generate those files.
Specify your config in bin/record-http-mocks.js and
npm run record-http-mocks
You can even make a mocked build version of the app.
Warning: Like in development, mocked requests will be intercepted (won't go to the server) and you will be shipping mocks (and the code that implements the mocking part) to your bundle.
This could be used for e2e testing purposes.
npm run build:mock
Online demo of the mocked version of the app
The goal of this project is to focus on how to put the pieces together to make a front-end app, to focus on architecture, not get stuck on webpack configurations 😉.
Toolkits are becoming more popular. Developers are tending to use them or make their own.
Since create-react-app is the most popular toolkit in the react community, I chose this one, with a challenge/constraint: NOT TO EJECT.
- So that when you dive in the project, you don't have any more overhead from enforcing any weird configuration or tool
- To challenge some use-cases where people tell you that you should eject when you can still remain
There are some points that I couldn't address with an unejected create-react-app:
- Not being able to add babel-presets/babel-plugins: usually I use a few ones such as:
- babel-plugin-dev-expression: I use invariant, it removes development checks added for development
- Not being able to alias modules via webpack config: when hacking/forking a module, it can come handy. This is why there is a src/libs folder (temporary).
- Use
HtmlWebpackPlugin
andwebpack.DefinePlugin
to expose vars generated on the fly (such as retrieving the git hash, the date of the build ...) - I managed to do that with bin/expand-metadatas.js.
For those who want to play with webpack configurations, you can access a starter kit I open sourced: topheman/webpack-babel-starter.
People often choose Redux before they need it. “What if our app doesn’t scale without it?” Later, developers frown at the indirection Redux introduced to their code. “Why do I have to touch three files to get a simple feature working?” Why indeed!
People blame Redux, React, functional programming, immutability, and many other things for their woes, and I understand them. It is natural to compare Redux to an approach that doesn’t require “boilerplate” code to update the state, and to conclude that Redux is just complicated. In a way it is, and by design so.
You Might Not Need Redux by Dan Abramov
I wanted to show that you don't necessarily need redux to make a web app like this one.
Also, not adding redux kept the codebase more agnostic.
Maybe in further versions, I will add redux while adding other features.
In every enterprise app, you're using some kind of UI Kit (whether it is homemade or based on libraries like material-ui, semantic-ui or bootstrap).
Working with such a library was part of the constraints I set.
I had never used material-ui
, it was the opportunity of testing it (and also test the css-in-js
paradigm that I knew of, but never coded with).
Recompose is a "React utility belt for function components and higher-order components" that you're likely to run into if your coding on React projects.
material-ui
relies on it, so at least a part of the library is already shipping into the final bundle. That way, i get to show some code you might encounter on other projects.
This project is a work in progress, here are some of the next features I'll be working on:
- Test pluging an other
css-in-js
library tomaterial-ui
(default one is jss) - Form management use case (to show a more advanced way of state management - maybe using redux ?)
- Add some i18n (since the content of the app is all in english, first find some adapted feature to apply it)
- Add prettier git-hook on travis
- Try out React Suspense (next versions of React)
- Upgrade
react-scripts
/ use webpack 4 ?