# clone and change cwd
git clone https://github.com/majodev/aaa-frontend-demo.git
cd aaa-frontend-demo
# checkout the initial (fixed) commit
git checkout fc9e950098e735d8ab547e886b5c54487b114398
git checkout -b <your username, e.g. mranftl>
# install the project dependencies
yarn
# open VSCode (eventually disable "files.autoSave": "afterDelay" in your VSCode config)
code .
# start the dev-server
yarn start
# let's get familiar with the project...
All the following sections are bound to specific commits e.g. fc9e950098e735d8ab547e886b5c54487b114398
related to the aaa-frontend-demo git repo and are marked accordingly at the beginning of the section (the end result).
80aa8d58a5a8ce3babfb7e6091e081b15ad05f8f
- react-router-dom npm package
yarn add react-router-dom @types/react-router-dom
- react-router Quickstart
09287ec060c375cc1cc472442e4e93ed6cb16b3c
- Understanding the React Component Lifecycle
- React.Component (facebook)
- State and Lifecycle (facebook)
componentDidMount() {}
- Handle events by arrow functions in React app
fetchRemoteTime = async () => {}
!==async fetchRemoteTime() {}
- An arrow function does not create its own
this;
thethis
value of the enclosing execution context is used. see MDN Arrow functions
- An arrow function does not create its own
12ea8992dac0bd42499f2fd6213a215bc5564376
- mui.Drawer
- undocked
- Link to outside
- mui.AppBar
- Your first styled-component
87b9bb0df15b3b0666468151ef2c41ddaed2f088
- previous
StyledLink
MenuItem to new ComponentMenuItemLink
- Functional and Class Components (facebook)
- Stateless Component (no lifecycle methods, no state, no class, just a function receiving props returning a react element)
05e493e96a5e64c425dc3393c509356ae5e7f275
- Context (facebook advanced guide)
- Decorators (TypeScript)
- Advanced Types:
Partial
and Union Types @i18n.injectIntl
+i18n.InjectedIntlProps
- TypeScript strict mode
this.props.intl!.formatMessage(...)
- TypeScript strict mode
FormattedMessage
works because of the set context (IntlProvider
), similar to how to:Link
works because ofRouter
contextmui.*
works because ofMuiThemeProvider
context
- Higher-Order Components (facebook advanced guide) —>
injectIntl
ac0f7de42795150e884212472096d254304c5007
- Casting with TypeScript: We can opt out of compile time safetly for i18n key anytime. This might be useful for mapping paths to i18n keys
- react-router
location
inject viawithRouter
andPartial<RouteComponentProps<any>>
- This works with TypeScript Generics under the hood
/
—>path.home
/beers
—>path.beers
/anything
—>path.anything
/anything/anotherthing
—>path.anything.anotherthing
7471b31689698070ad571790daa5171aad48a6c4
Attention: Only
3600
requests per hour~1 req/sec
are allowedSpecial header:
x-ratelimit-remaining
—>remainingRequests
state
- json2ts to get the
IBeer
and child TypeScript interfaces. - Install lodash
yarn add lodash @types/lodash
(map over a) - Let's use mui.List to show
name
,tagline
anddescription
of aBeer
50d740862a6096ca79f04f00e4cdadc80c364847
- We want a new unrelated route and use react-router url parameters
RouteComponentProps
First discuss some background on application state (setState
vs Flux vs Redux vs MobX)...
17b0ecf9590007716b0a1e6bd059c7f126401b4e
Let's share beer information between the 2 routes —> Application-Wide singleton store for our beers
- mobx —
yarn add mobx mobx-react
@observable
@action
@observer
01a86ce7d15ebe89b3b7dd1f829ed7d8ee916293
- Reuse already fetched beer if possible
selectBeer(id: number)
ES6 / TypeScript Spread Operator
So you've shown your current prototype your project owner come back with a list of bugs and new features:
- (7.3.1, code smell): Remove duplicated code (duplicate
loading
flags, fetch handling) - (7.3.1, bug): Go from single beer site to beer list should actually work
- (7.3.1, bug): Beer should not be visible if we revisit the site (while loading a new single beer)
- (7.3.1, feature): Sort beer list alphabetically
- (7.3.2, feature): Single Beer or beers list were already loaded? Use that!. However still only load the single beer if on the detail page!
- (7.3.2, feature): Single shared loading information if the store is currently fetching something from any endpoint.
- (7.3.2, feature): Counter how many requests were currently successfully made by the store
- (7.3.3 feature): Error handling: Alert on fetch errors (
mui.SnackBar
): e.g. id was not found, id is not a number or on request errors - (7.3.3 feature): The beer detail page should use the same header as the beer list page.
d049151912ca31915cbcbe7cd6c7f759f2d86948
- Start by defining a single
getBeersApi(id?: number): Promise<IBeer[]>
fn - All requests pipe through
unionAndSortBeers(newBeers: IBeer[]):IBeer[]
_.sortBy
_.unionWith
ca9ebb5d651ed2edd46f3b55aeab390749beb2d5
- Track by dynamically populated
uriMap
ObservableMap
@computed
_.reduce
c5ae2408da3b06d516c42782ccac98229242f103
- We need to have an Alert like functionality if something is not right.
try/catch
handle this in our state store but allow to dismiss it
- Furthermore we need to further split our components to show this alert (and the title heading) on both routes —> Child Routes
Route
can appear anywhere in your component, but not as direct a
mui.Snackbar
21e849d7b4093be41043536fbe1b61594c91f19a
Lets style the beer detail!
mui.Card
mui.Chip
mui.FloatingButton
- Material Icons -
navigation/chevron-left/right
- React
style
13da51ba24cfab374bd168c0ae109281c0861cc8
80c7ff8dc8b6ad24425acefe035b16b98e8815ab
- Use a combination of
@observable
,@computed
._.filter
and_.includes
- Icon:
action/thumb-up
mui.CardActions
insidemui.Card
- Icon Example of
mui.RaisedButton
(Icon only, 2 states!) ListIcon
userightIcon
f00f4859cf90c65c7b72d638d0dfe5ecc0addf41
- ES6 …spread
- React JSX spread
- inline conditionals
740b3541424f89bbc1d0b32aa84dcd098d358afe
- In preparation for server side loading: what if we have multiple liked beers in our store, but no information on them yet
- autorun: Autoruns are about initiating effects, not about producing new values.
_.remove
fix.
7ace09826fdd5df835fae608cb1d4c487efbfeca
- View state vs. global state
mui.Dialog
forbeerState.wipe()
- TextInput and Submit-Button
- Submit Button should only be enabled if the form was changed since the last submit
- Comment validations (feedback must be shown to the user):
- Should not start or end with a space
- Should have max length of 35 characters
- Should not have multiple spaces
- An empty comment is allowed and will trigger a delete of the comment
6e275cdf9f97e85f6c84f2f7b6daf9e0ff9d9f09
- Let's allow to add a simple multiline comment for a beer and finally submit that value to the store
- Uncontrolled Components vs Controlled Components
mui.TextField
— see Controlled Example(e: React.ChangeEvent<{ value: string }>) => {...}
d642364f086ff3873eb44b1712eea75bfa74c285
- Another map in our state.
bf055ea3214782f00dbffa179eae6c1e3f953e08
- Env update (restart
yarn start
) - Add a
authState
store - Integrate with
POST /api/v1/auth/token
andGET /api/v1/auth/profile
(admin/demo
) - Special
guest
grantType issues BeareraccessTokens
that will never expire.
bc68b97edc15ff6d3fe63888cf467634d8849942
PATCH /app/v1/user/profile
allows to set save anyJSON
data
on the user profile- your likes / comments count
- Push local changes to the server if we are authenticated...
- We won't get it again (no synchronization will take place, local state will always overwrite user server state)
- Could be easily done while rehydration, same as getting the user profile (testing the connection) in the
authState
bcf43a9e0e077ad4c1657ad141587675105ea7a6
GET /app/v1/beers-info
allows to retrieve all updated likes and comments from all beers- Public endpoint, try it out.
type IBeersInfo = {
globalLikes: { [id: string]: number },
globalComments: { [id: string]: { user: string, comment: string }[] }
};
Let's try out an easy way to host your static bundle for free (requires a working GitHub account).
TL;DR: