Skip to content
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
Cannot retrieve contributors at this time

aaa-frontend-demo guideline

0. Clone the sample repository

# clone and change cwd
git clone
cd aaa-frontend-demo

# checkout the initial (fixed) commit
git checkout fc9e950098e735d8ab547e886b5c54487b114398
git checkout -b <your username, e.g. mranftl>

# install the project dependencies

# 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).

1. Add a dependency to handle Routing


2. Lifecycle and event handlers


2.1 Component Lifecycle

2.2 Bound functions (event handlers)

3. Material UI: Drawer and AppBar


4. Stateless Components


5. Decorators, advanced types, context and props.children: i18n


5.1 Make the location context available for i18n


  • 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 via withRouter and Partial<RouteComponentProps<any>>
  • This works with TypeScript Generics under the hood
    • / —> path.home
    • /beers —> path.beers
    • /anything —> path.anything
    • /anything/anotherthing —> path.anything.anotherthing

6. Let's have some beers!

6.1 Implement beers remaining counter and a simple list


Attention: Only 3600 requests per hour ~1 req/sec are allowed

Special header: x-ratelimit-remaining —>remainingRequests state

6.2 Implement a beer detail route


7. State Management

First discuss some background on application state (setState vs Flux vs Redux vs MobX)...

7.1 Refactor BeersRoute to observe state change from a global store


Let's share beer information between the 2 routes —> Application-Wide singleton store for our beers

7.2 Access raw beer information in beerDetailRoute


  • Reuse already fetched beer if possible
  • selectBeer(id: number)

ES6 / TypeScript Spread Operator

7.3 Bugs and features...

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.
7.3.1 Remove code smell, deselectBeer and sort


  • Start by defining a single getBeersApi(id?: number): Promise<IBeer[]> fn
  • All requests pipe through unionAndSortBeers(newBeers: IBeer[]):IBeer[]
  • _.sortBy
  • _.unionWith
7.3.2 @computed and ObservableMap


7.3.3 Same title bar on both beer related pages and error alerts


  • 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

8. Beer detail page


Lets style the beer detail!

9. Persist state client side and flexbox alignments


10. Like (& unlike) beers


  • Use a combination of @observable, @computed.
  • Icon: action/thumb-up
  • mui.CardActions inside mui.Card
  • Icon Example of mui.RaisedButton (Icon only, 2 states!)
  • ListIcon use rightIcon

10.1 Show liked beers in sidebar and add a counter


10.2 autorun and caching (+ fix unlike bug)


  • 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.

10.3 (optional) wipe dialog


  • View state vs. global state
  • mui.Dialog for beerState.wipe()

11. Forms: Comment your beer

  • 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

11.1 BeerCommentForm


11.2 Connect to state


  • Another map in our state.

12. Login: Guest access token


13. Save likes and comments on the server


  • PATCH /app/v1/user/profile allows to set save any JSON 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

14. Show global liked count and other user comments


  • 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 }[] }

15. Deploy to GitHub Pages (optional)

Let's try out an easy way to host your static bundle for free (requires a working GitHub account).

Reference Docs


  • Install the gh-pages package through yarn: yarn add --dev gh-pages.
  • Add a homepage property to package.json. See here.
  • Add scripts for predeploy and deploy in your package.json. See here.