Building Scalable, Maintainable Apps Using TypeScript and React
Kamran Ayub
patterns and practices
๐จโ๐ฉโ๐ฆ ๐ถ๐ถ
๐ฒ๐ฎ๐จโ๐ป๐๐ธ
Work at Target
What do I want?
To make my apps easier to maintain
To make my apps easier to scale
Scalable
?
Scalable
Easy to extend
ย
Easy to collaborate on
ย
Easy to reuse code
Maintainable
Easy to refactor
ย
Easy to understand
ย
Easy to test
A familiar story...
TypeScript
JavaScript that scales
- Static type checking
- Fantastic editor tooling
- Refactor-friendly code
React
Component-based presentation
- Small surface area
- Reusable components
- Functional characteristics
- Great performance
A better way is possible
ktomg.com
- Built in .NET
- Hybrid app
- Originally JavaScript & Knockout.js
- Migrated to TypeScript & React
- Heavily interactive UI
Based on a true story
(work in progress)
Concepts
- Store - Single source of truth for app
- State - The data representing the app at a point-in-time
- Actions - Object with a "type" property
- Action Creators - Small functions that create actions
- Reducersย - Take actions and update Redux state
Folder Layout
State & Reducers
Actions
Props & State
Action Creators
+ bonus: Jest / CSS-in-JS
Folder layout
(previous)
- Files are spread out
- Components folder doesn't scale well
- Fine for a small site
Folder Layout
Folder layout
(new)
- Proposed by Ryan Florence
- Screen organization
- Scalable nesting
- Better collaboration
- Colocate related files
- Closer imports
Folder Layout
Module Resolution
Not scalable as screens nest further
Folder Layout
Module Resolution
Maintains F12 Go to Definition behavior
Folder Layout
Module Resolution
Folder Layout
Module Resolution
Folder Layout
There's an open issue for emulating "module directories" functionality from Webpack/Jest to allow paths like:
Folder Layout
- โ Scalable folder structure
- โ Avoids ../../ nesting
- โ Supports TypeScript and dev tools
ย
- ๐ No module dirs support yet
Folder Layout
Redux State
"Bottom up" state
State & Reducers
No need to define an entire representative "State" interface. Just export what it will end up being (typeof)
Folder Layout
Redux State
"Bottom up" state
Each screen will define its state shape
State & Reducers
Folder Layout
Redux State
"Bottom up" state
State & Reducers
Folder Layout
Reducers
Combining reducers
State & Reducers
Folder Layout
Reducers
Combining reducers
State & Reducers
Folder Layout
Reducers
Combining reducers
State & Reducers
Folder Layout
Reducers
Combining reducers
State & Reducers
Folder Layout
Reducers
Testing reducers
State & Reducers
Folder Layout
State & Reducers
State & Reducers
- โ Maximizes type inference
- โ "Bottom-up" composable state
- โ Screens manage their own state
- โ Using "shared" folder, you can share state
- โ Using RecursivePartial<T> type helper
Folder Layout
Actions
Using constants and typeof
A bit wordy, but pays off at scale
Actions
Folder Layout
State & Reducers
Actions
Action type helpers
Actions
Folder Layout
State & Reducers
Actions (Discriminated)
Actions
Folder Layout
State & Reducers
Actions (Mapped)
Actions
Folder Layout
State & Reducers
Actions (Mapped)
Creating reducer action map helpers
Actions
Folder Layout
State & Reducers
Actions (Mapped)
Actions
Folder Layout
State & Reducers
Actions
Actions
- โ Strongly-typed actions
- โ Reducer action handlers are typed
- โ Reusable type utilities
- โ If actions or state change, reducers break
Folder Layout
State & Reducers
Action Creators
Action Creators
Folder Layout
Actions
State & Reducers
Action Creators
Dispatching actions
mapDispatchToProps makes it easier to test component in isolation
Action Creators
Folder Layout
Actions
State & Reducers
Action Creators
All type-safe with intellisense
Action Creators
Folder Layout
Actions
State & Reducers
Action Creators
Action Creators
- โ Action creator utility
- โ Dispatch is strongly-typed
- โ Ensure actions invoked with correct args
- โ Testing component is easier
- โ Isolates action signature changes from components
ย
- ๐ย Dispatch typing is wordy, repetitive, annoying
Folder Layout
Actions
State & Reducers
Props & State
(stateful, non-redux)
Props & State
Folder Layout
Actions
Action Creators
State & Reducers
Props & State
(stateless, non-redux)
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Props & State
(render prop pattern)
Can pull into type alias and share
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Props & State
(redux state props)
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Need a way to get "dispatch" and separate Redux state props vs. "own" props
Extract stateless type alias to encapsulate our State and reduce DispatchProp usage
Props & State
(redux state props)
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Same as stateless, create encapsulated Redux component type
Props & State
(redux class component)
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Split Redux connected component from "raw" component for easier isolated testing
Props & State
(redux class component)
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Props & State
Redux type utilities
Folder Layout
Actions
Props & State
Action Creators
State & Reducers
Props & State
Folder Layout
Actions
Props & State
- โ Splitting OwnProps vs. StateProps
- โ Reduce excessive type intersections
- โ Reusable type utilities
- โ Render prop pattern typing
- โ Easier testing of Redux
Action Creators
State & Reducers
BONUS SLIDE
Folder Layout
Actions
BONUS
Action Creators
State & Reducers
Using emotion CSS-in-JS
Doesn't really have any "weirdness" with TypeScript
ย
For Jest testing and Enzyme, reference the code for deeper examples of async testing / Redux testing
All Together Now!
There's always room for improvement
Thanks for listening!
Maintainable Apps with TypeScript & React (MDC 2018)
By Kamran Ayub
Maintainable Apps with TypeScript & React (MDC 2018)
Given at MDC 2018
- 1,481