Our Practices of Building a React Web App


ZHUORAN LI

GitHub: @lzhuor

🎵 Clarinet, 🏀 Basketball, 🎿 Ski

🤯 What's happening?
The "library"

Small Scope
Evan You on Vue.js: Seeking the Balance in Framework Design | JSConf.Asia 2019
"Warning"

- Opinionated
- Mostly for SPA
- Different thoughts are more than welcome!
Delivery of Design





😭 Sketch




Components
Core
Frontend

workspace
Monorepo
Check out once, develop all!

Components
- Shared components, usable across scenes, modals and layouts that are behaving in the same way
- Generic, in the way that different components could potentially use them with specific implementations



Core
- Contains all the Redux & Saga modules shared across the platform
- composing data of micro-services or BFF*
- model the resources of your domain
- API interactions
- Only needed if you really know why you need it, usually for complex domain models
- client-side Cryptowallet is a good usecase

Core

Redux & Sagas

Core

API Client


Frontend
- The actual web app implements business logics

👨💻


workspace
Monorepo
{
"test": "cross-env yarn wsrun test:build --fast-exit && yarn wsrun test"
}
{
"test:components": "yarn workspace silver-components test"
}
Styling

- Inline props
- Flexibility
- Generic HTML tags
- Wrap existing components
- Global styles
- Theme

styled-components
const Container = styled.div`
background: ${props => props.bgColor};
`
const Container = styled(BaseContainer)`
background: red;
`
Use defined name of color instead of hex code



Layouts


<PublicLayout />

<SilverLayout />
- Authentication

const ShareFormContainer = styled.div`
margin: 3.2rem 0;
display: flex;
align-items: center;
${media.maxWidth.mobile`
margin-bottom: 0;
flex-direction: column;
`};
`
- Consistent
- breakpoints of Grid system
- breakpoints of Media Query (in CSS)
- <MediaContext /> API (in JSX)
Responsiveness

const { mobile } = useContext(MediaContext)
Responsiveness

Bundle Analysis

"scripts": {
"analyze": "cross-env-shell ANALYZE=true NODE_ENV=production webpack-cli --config webpack.config.prod.js"
}
Code Split
- By route (URL)
- By frequency
- By feature
- By region / language


💪 Alternative (SPA): React.lazy & <Suspense />
https://reactjs.org/docs/code-splitting.html
Code Split
import Loadable from 'react-loadable'
import LoadableLoading from 'scenes/Common/LoadableLoading'
const Declaration = Loadable({
loader: () =>
import(
/* webpackChunkName: "declarations" */ 'scenes/Onboarding/Declaration'
),
loading: LoadableLoading
})

Code Split

Survey.sg.a9dn1kdf7.js
Survey.us.f6alekd5m.js
Tooltips


Declare once, use anywhere!

Layout Component
Modals


Layout Component
Modals


HOC
Declare once, use anywhere!
Error Tracking
- React Render
- <ErrorBoundary/>
- Anything else in JavaScript
- selectors
- reducers
- helpers, services, providers
- and more...
- Redux & Redux-Saga
- use middleware

- User
- HTML Events & Redux Actions
- URL, Device, IP, Browser
- Stack Trace
- API Interactions
- App Version
- etc.
Sentry

Staging
Sample
Sentry

Staging
Sample
Sentry

Staging
Sample
Internationalisation
- Translation
- en.json
- zh.json
- Formatting
- Money, Date, Number, Plurals etc.

Internationalisation
- String
- HTML (not encouraged)
- React Component
- Templating


plugin

Coding Styles
FP with Ramda.js
- Immutable
- Expressive
- Purity, code is easy to test
- Conceptually compatible with HOC, Functional Component, Redux
Migrating to Hooks
- Lifecycle
- Local State
- Context consumer
- react-redux connector
- Custom HOCs
Note: One challenge you may encounter with useEffect Hooks
react-redux v7.1 Hooks

Migrating to Hooks
useAction


Migrating to Hooks
useNotifications


Redux-Form

<Context />

Forms

20+
QA

