salsita / react-training

react training app
5 stars 4 forks source link

Syllabus

https://docs.google.com/presentation/d/1w8HRmAK2HB5PuwOB0HdFeGbvntsvM3-rvHdNOPZ3lBs

Before you begin

Install all dependencies and link executables to child projects with npm i in the exercises folder.

Exercises

Exercise #1

The main purpose of this exercise is to try React and its stateful components implemented with React Hooks.

Header component

Location: src/modules/root/components/header.tsx

HeaderProps:

{
  title: string
}

This component just renders a heading (h1) with a string taken from the title property.

UserTypes

Location: src/modules/users/user-types.ts

This file contains definitions of user interfaces.

UserList component

Location: src/modules/users/components/user-list.tsx

This component renders a list of the users saved in the state and two buttons to add two different users.

Exercise #2

The main purpose of this exercise is to try stateless components.

UserTypes

Location: src/modules/users/user-types.ts

UserList component

Location: src/modules/users/components/user-list.tsx

Props:

{
  users: User[],
  addUser: AddUser
}

The functionality is the same like in the previous exercise. The only difference is that the logic will be outside the file.

Index file

Location: src/index.tsx

Move logic from the old UserList into the index file. All application data will be in a global object.

Root component

Location: src/modules/root/components/root.tsx

Props:

{
  title: string,
  users: User[],
  addUser: AddUser
}

Since we moved the logic into the index file and the Root component receives all necessary props, we need to send props into Header and UserList.

Additional task

Try 3 different versions of the Header component and see when they get rendered

  1. Created as a class that extends React.Component
  2. Created as a class that extends React.PureComponent
  3. Created as a function

Exercise #3

The main purpose of this exercise is to try Redux.

UserActions

Location: src/modules/users/user-actions.ts

This file defines actions and action types.

usersReducer

Location: src/modules/users/users-reducer.ts

State:

{
  title: string,
  users: User[]
}

The logic from addUser function from the previous exercise will be in this reducer.

rootReducer

Location: src/modules/root/root-reducer.ts

This is the main reducer of the whole app. The main purpose is to combine all reducers from all modules into a single reducer.

Index file

Location: src/index.tsx

Configure all necessary things for redux.

Exercise #4

The main purpose of this exercise is to try React Redux and Redux Toolkit.

Header component

Location: src/modules/root/components/header.tsx

The same component like in the previous exercise.

Props:

{
}

UserList component

Location: src/modules/users/components/user-list.tsx

The same component like in the previous exercise.

Props:

{
}

Root component

Location: src/modules/root/components/root.tsx

Props:

{
}

Users slice

Location: src/modules/users/users-slice.ts

The same actions and reducer, but created with Redux Toolkit.

UserActions

Location: src/modules/users/user-actions.ts

This file is no longer needed. The user actions are now generated in src/modules/users/users-slice.ts.

usersReducer

Location: src/modules/users/users-reducer.ts

This file is no longer needed. The addUser reducer was moved to src/modules/users/users-slice.ts.

Index file

Location: src/index.tsx

Currently, we need only store and we need to call ReactDOM.render directly with Provider component.

Exercise #5

The main purpose of this exercise is to try Reselect.

UsersSelectors

Location: src/modules/users/users-selectors.ts

Header component

Location: src/modules/root/components/header.tsx

The same component like in the previous exercise.

UserList component

Location: src/modules/users/components/user-list.tsx

The same component like in the previous exercise.

Exercise #6

The main purpose of this exercise is to try Redux-Saga, axios, and Express.

package.json

Location: package.json

Server file

Location: backend/server.ts

A simple express server that has 2 routes GET /users and POST /users.

TypeScript configuration for BE

Location: backend/tsconfig.json

A simple TypeScript configuration file.

API Client

Location: src/modules/api/api-client.ts

This file contains an API client with axios that is used to make requests to the BE server.

UsersEffects

Location: src/modules/users/users-effects.ts

This file defines all effect functions that perform the corresponding requests to API. We need only 2 effects right now - getUsers and addUser.

UsersSlice

Location: src/modules/users/users-slice.ts

We will need a new reducer to store fetched users into the state.

Users are added on the BE side, so the addUser reducer is not needed anymore, but the action type still is.

usersSaga

Location: src/modules/users/users-saga.ts

This file is used to create redux sagas that handle side effects to communicate with the BE server. We need 2 sagas to handle all API effects we have - getUsers and addUser.

rootSaga

Location: src/modules/root/root-saga.ts

This file simply starts all sagas that are needed in the whole application. Currently, we have only our own usersSaga.

Index file

Location: src/index.tsx

Configure all necessary things for redux-saga.

Exercise #7

The main purpose of this exercise is to try normalizr.

Server file

Location: backend/server.ts

The server adds skills and set the correct regnal number to every user.

The entity interfaces are

interface Skill {
  id: string // e.g. skill-1
  name: string
}

interface UserSkill {
  skill: Skill
  level: number
}

interface User extends UserName {
  id: string; // e.g. user-1
  regnalNumber: number // use Arabic numerals
  skills: Array<UserSkill>
}

Entities Schema

Location: src/modules/entities/entities-schema.ts

This file contains normalizr schema of our entities.

Entities types

Location: src/modules/entities/entities-types.ts

This file contains type definitions for normalized user data.

usersSlice

Location: src/modules/users/users-slice.ts

State:

{
  title: string,
  userIds: string[]
}

Entities slice

Location: src/modules/entities/entities-slice.ts

This file contains an entities reducer, which manages the entities repository.

rootReducer

Location: src/modules/root/root-reducer.ts

The created entities reducer needs to be added into the root reducer.

entitiesSaga

Location: src/modules/entities/entities-saga.ts

This file contains a saga which normalizes data and stores them to the entities state.

usersSaga

Location: src/modules/users/users-saga.ts

Currently, the same denormalized data that comes from the BE server are stored in the state. We need to normalize the data from response and store them in the entity repository.

EntitiesSelectors

Location: src/modules/entities/entities-selectors.ts

Since data are stored in the normalized form in the state, we need to denormalize them for easier access to values.

UsersSelectors

Location: src/modules/users/users-selectors.ts

The users reducer doesn't store the entity data, it stores ids only.

UserList component

Location: src/modules/users/components/user-list.ts

Exercise #8

The main purpose of this exercise is to try router5 and @salsita/react-crud.

Attention

The solution of this exercise (08-router5) uses a separate set of dependencies. You can run npm install in the corresponding directory to install them.

Server file

Location: src/server.js

Add a route to fetch a single user.

rootReducer

Location: src/modules/root/root-reducer.js

We need to add all required reducers into the root reducer.

Routes

Location: src/router/routes.js

This file contains names and configuration of routes.

Index file

Location: src/index.js

Use buildRouter and buildStore functions for easier configuration of redux, router5, and redux-saga.

UserDetail component

Location: src/modules/users/components/user-detail.js

Props:

{
  userDetail: {
    firstName: string,
    lastName: string,
    regnalNumber: string,
    skills: Array<{
      skill: {
        name: string
      },
      level: number
    }>
  }
}

This component displays a user detail with skills information.

UsersRoute component

Location src/modules/users/components/users-route.js

Props:

{
  route: {
    name: string
  }
}

This component takes care about the proper routing in the users module.

UsersList component

Location: src/modules/users/components/users-list.js

We need to add links to the detail page. Navigate to the detail page when the user clicks on the first name.

Root component

Location: src/modules/root/components/root.js

Use the Route component from @salsita/react-router for easier routing. You can also use ApiErrorToast and ApiLoader to display a basic error toast and loading spinner. The data for both components are automatically stored in the state from @salsita/react-api.

CRUD Saga file

Location: src/modules/crud/crud-saga.js

This file contains two important functions for the CRUD module - mapEntityToSaveParams and mapRouteToFetchParams. Both of them return params that are used for saving or fetching entities.

CRUD Entities

Location: src/modules/crud/crud-entities.js

This file has only string constants with entity names for mapEntityToSaveParams

CrudSelectors

Location: src/modules/crud/crud-selectors.js

The CRUD module takes care about automatic storing of entity ids. Since the ids won't be in the usersReducer anymore, we need to slightly update UsersSelectors and move them into CrudSelectors.

UsersSelectors

Location: src/modules/users/users-selectors.js

rootSaga

Location: src/modules/root/root-saga.js

Start crudSaga to automatically fetch entities.

UsersEffects

Location: src/modules/users/users-effects.js

We need a new effect to fetch a single user. Also, we should use wrapApiCall from @salsita/react-api for proper error handling.

usersSaga

Location: src/modules/users/users-saga.js

The CRUD module handles entity fetching so we don't need the getUsers saga anymore. Use the saveEntity saga from @salsita/react-crud for better error handling and fetchEntities to refresh the user list.

UsersActions

Location: src/modules/users/users-actions.js

We don't need the USERS_LOADED action anymore.

usersReducer

Location: src/modules/users/users-reducer.js

State:

{
  title: string
}

Exercise #9

The main purpose of this exercise is to try Redux Form.

Attention

The solution of this exercise (09-forms) uses a separate set of dependencies. You can run npm install in the corresponding directory to install them.

Server file

Location: src/server.js

Add routes that updates a user and fetches skills. Modify the route that saves a new user.

rootReducer

Location: src/modules/root/root-reducer.js

We need to add the form reducer into the root reducer.

Routes

Location: src/router/routes.js

UsersEffects

Location: src/modules/users/users-effects.js

We need new effects to update a user and fetch all skills.

UsersActions

Location: src/modules/users/users-actions.js

Currently, we have only one action called ADD_USER that is dispatched when a user clicks on one of the buttons. Since we will use this action to create or update a user, let's rename it to SAVE_USER.

CRUD Saga file

Location: src/modules/crud/crud-saga.js

We will need a list of all skills to display them in the create/edit form. We also have a new effect called updateUser so we can use it in mapEntityToSaveParams.

CrudSelectors

Location: src/modules/crud/crud-selectors.js

UserForm component

Location: src/modules/users/components/user-form.js

This form component has fields for firstName, lastName, and skills where a single user can have multiple skills.

UserCreate component

Location: src/modules/users/components/user-create.js

Props:

{
  saveUser: (formData: object) => void
}

This component just renders the UserForm component to create a new user.

UserDetail component

Location: src/modules/users/components/user-detail.js

Props:

{
  userDetail: {
    firstName: string,
    lastName: string,
    regnalNumber: string,
    skills: Array<{
      skill: {
        name: string
      },
      level: number
    }>
  },
  saveUser: (formData: object) => void
}

This component just renders the UserForm component with initialValues to edit a user.

UsersList component

Location: src/modules/users/components/users-list.js

Props:

{
  users: Array<{
    id: number,
    firstName: string,
    lastName: string,
    regnalNumber: string
  }>
}

UsersRoute component

Location src/modules/users/components/users-route.js

We want to display the UserCreate component in a modal dialog while the users list is shown in the background.

usersSaga

Location: src/modules/users/users-saga.js

There are couple of things we need to update in our sagas.