Skip to content
Permalink
master
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 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).

1. Add a dependency to handle Routing

80aa8d58a5a8ce3babfb7e6091e081b15ad05f8f

2. Lifecycle and event handlers

09287ec060c375cc1cc472442e4e93ed6cb16b3c

2.1 Component Lifecycle

2.2 Bound functions (event handlers)

3. Material UI: Drawer and AppBar

12ea8992dac0bd42499f2fd6213a215bc5564376

4. Stateless Components

87b9bb0df15b3b0666468151ef2c41ddaed2f088

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

05e493e96a5e64c425dc3393c509356ae5e7f275

5.1 Make the location context available for i18n

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

7471b31689698070ad571790daa5171aad48a6c4

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

50d740862a6096ca79f04f00e4cdadc80c364847

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

17b0ecf9590007716b0a1e6bd059c7f126401b4e

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

7.2 Access raw beer information in beerDetailRoute

01a86ce7d15ebe89b3b7dd1f829ed7d8ee916293

  • 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

d049151912ca31915cbcbe7cd6c7f759f2d86948

  • 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

ca9ebb5d651ed2edd46f3b55aeab390749beb2d5

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

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

8. Beer detail page

21e849d7b4093be41043536fbe1b61594c91f19a

Lets style the beer detail!

9. Persist state client side and flexbox alignments

13da51ba24cfab374bd168c0ae109281c0861cc8

10. Like (& unlike) beers

80c7ff8dc8b6ad24425acefe035b16b98e8815ab

  • 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

f00f4859cf90c65c7b72d638d0dfe5ecc0addf41

10.2 autorun and caching (+ fix unlike bug)

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.

10.3 (optional) wipe dialog

7ace09826fdd5df835fae608cb1d4c487efbfeca

  • 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

6e275cdf9f97e85f6c84f2f7b6daf9e0ff9d9f09

11.2 Connect to state

d642364f086ff3873eb44b1712eea75bfa74c285

  • Another map in our state.

12. Login: Guest access token

bf055ea3214782f00dbffa179eae6c1e3f953e08

13. Save likes and comments on the server

bc68b97edc15ff6d3fe63888cf467634d8849942

  • 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

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

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

TL;DR:

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