Reactjs: Difference between revisions

From Training Material
Jump to navigation Jump to search
 
(121 intermediate revisions by 2 users not shown)
Line 13: Line 13:
{{Can I use your material}}
{{Can I use your material}}


== Introduction ==
== Introduction ==
* Developed by engineers at Facebook
* Developed by engineers at Facebook
** ''"A JavaScript library for building user interfaces"''
** ''"A JavaScript library for building user interfaces"''
Line 21: Line 21:
** Other '''memory-heavy''' tasks without slowing down
** Other '''memory-heavy''' tasks without slowing down


=== Intro Con't ===
=== Intro Con't ===
* Declarative
* Declarative
* Component-Based
* Component-Based
* '''Reusable''' and independent
* '''Reusable''' and independent


==== Declarative ====
==== Declarative ====
* '''Declarative''' views make the code more '''predictable''' and easier to '''debug'''
* '''Declarative''' views make the code more '''predictable''' and easier to '''debug'''
* Very easy way to create interactive UIs
* Very easy way to create interactive UIs
Line 32: Line 32:
* '''Efficient update and render''' of the right components when the data changes
* '''Efficient update and render''' of the right components when the data changes


==== Component-Based ====
==== Component-Based ====
* Encapsulated components that '''manage their own state'''
* Encapsulated components that '''manage their own state'''
** Compose them to make complex UIs
** Compose them to make complex UIs
Line 39: Line 39:
** In the same time keeps state '''out of the DOM'''
** In the same time keeps state '''out of the DOM'''


==== Reusable ====
==== Reusable ====
* '''Independent''' from technology stack used in the whole project
* '''Independent''' from technology stack used in the whole project
* We can develop new features without rewriting existing code
* We can develop new features without rewriting existing code
* Can '''render on the server''' (for example using ''Nodejs'')
* Can '''render on the server''' (for example using ''Nodejs'')
* Can power mobile apps using '''React Native'''
* Can power '''mobile''' apps using '''React Native'''


== Design principles behind React ==
== Design principles behind React ==
* JSX
* '''JSX'''
* Rendering Elements
* '''Rendering''' Elements
* Components and '''props'''
* Components and '''props'''


=== JSX ===
=== JSX ===
* Is not valid JavaScript (web browsers can't read it)
* Is not valid JavaScript (web browsers can't read it)
* Syntax extension for JavaScript
* Syntax extension for JavaScript
Line 59: Line 59:
* Have to be compiled into regular JavaScript
* Have to be compiled into regular JavaScript
* Is optional and not required to use in React
* Is optional and not required to use in React
* <small>https://reactjs.org/docs/introducing-jsx.html</small>
* <small>https://react.dev/learn/writing-markup-with-jsx</small>
** <small>https://react.dev/learn/javascript-in-jsx-with-curly-braces</small>


=== Rendering Elements ===
=== Rendering Elements ===
* The smallest building block of React apps
* The smallest building block of React apps
* Rendering an element into the DOM
* Rendering an element into the DOM
* Updating the rendered element
* Updating the rendered element
* Updated will be only that what's necessary
* Updated will be only that what's necessary
* <small>https://reactjs.org/docs/rendering-elements.html</small>
* <small>https://react.dev/learn/add-react-to-an-existing-project#step-2-render-react-components-anywhere-on-the-page</small>


== Building your first component ==
== Building your first component ==
* Component let split the UI into independent, reusable and isolated pieces
* Component lets split the UI into independent, reusable and isolated pieces
* Components are like JavaScript functions (conceptually)
* Components are like JavaScript functions (conceptually)
** Accept '''arbitrary inputs''' (called "props")  
** Accept '''arbitrary inputs''' (called "props")  
Line 84: Line 85:
         // const process = { 'env': { 'NODE_ENV' : 'development' } };
         // const process = { 'env': { 'NODE_ENV' : 'development' } };
     </script>
     </script>
     <script crossorigin src="https://unpkg.com/react@18.2.0/umd/react.development.js"></script>
     <script crossorigin src="https://unpkg.com/react@18.3.1/umd/react.development.js"></script>
     <script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.development.js"></script>
     <script crossorigin src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
     <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
     <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
   </head>
   </head>
Line 101: Line 102:
</syntaxhighlight>
</syntaxhighlight>


== How components work in React ==
== How components work in React ==


* Components implement a '''render()''' method
* Components implement a '''render()''' method
Line 109: Line 110:
** Input data passed into the component can be accessed by ''render()'' via ''this.props''  
** Input data passed into the component can be accessed by ''render()'' via ''this.props''  


=== Example 1 ===
=== Example 1 ===
<syntaxhighlight lang="ts">
<syntaxhighlight lang="ts">
         //// JSX code
         //// JSX code
Line 138: Line 139:
</syntaxhighlight>
</syntaxhighlight>


=== Exercise 1 ===
=== Exercise 1 ===
Rewrite it into React function (-:
Rewrite it into React function (-:
<!-- Solution
<!-- Solution
Line 146: Line 147:
-->
-->


== The component life cycle ==
== The component life cycle ==
* In application with many components
* In application with many components
** Components are destroyed  
** Components are destroyed  
Line 158: Line 159:
* [https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ "Common lifecycles"]
* [https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ "Common lifecycles"]


== Handling state in React ==
== Handling state in React ==
* State is similar to props
* State is similar to props
** But it is '''private''' and fully '''controlled by the component'''
** But it is '''private''' and fully '''controlled by the component'''
Line 167: Line 168:
** When a component's state data changes, the rendered markup will be updated by re-invoking '''render()'''
** When a component's state data changes, the rendered markup will be updated by re-invoking '''render()'''


=== Example 2 ===
=== Example 2 ===
<syntaxhighlight lang="ts">
<syntaxhighlight lang="ts">
//// JSX part
//// JSX part
Line 214: Line 215:
</syntaxhighlight>
</syntaxhighlight>


=== Exercise 2 ===
=== Exercise 2 ===
Guess what? (-!
Guess what? (-!
* Write function'ish version of it - of course!
* Write function'ish version of it - of course!
Line 235: Line 236:
-->
-->


=== Managing state ===
=== Managing state ===
* React on its own does not provide built-in support for state management
* React on its own does not provide built-in support for state management
* The React community uses libraries
* The React community uses libraries
** Redux
** Redux
** MobX
** MobX
** xstate
* React state vs. Redux state
* React state vs. Redux state


==== Redux ====
==== Redux ====
* Relies on synchronizing data through a centralized and immutable store of data
* Relies on synchronizing data through a centralized and immutable store of data
* Updates to that data will trigger a re-render of the application  
* Updates to that data will trigger a re-render of the application  
Line 249: Line 251:
* Because of the explicit nature, it is often easier to reason about how an action will affect the state of our program
* Because of the explicit nature, it is often easier to reason about how an action will affect the state of our program


==== MobX ====
==== MobX ====
* Relies on functional reactive patterns
* Relies on functional reactive patterns
** State is wrapped through observables and passed through as props
** State is wrapped through observables and passed through as props
Line 255: Line 257:
*** Simply marks state as observable.  
*** Simply marks state as observable.  
* Already written in TypeScript
* Already written in TypeScript
==== xstate ====
* Relies on event-driven programming, state machines, statecharts, and the actor model
** manages application and workflow state (fe, be, or any js run time environment)
** models logic as actors and state machines
* ''API'' <small>https://www.jsdocs.io/package/xstate</small>
* ''Generator'' <small>https://stately.ai/docs/generate-react</small>


==== React state vs. Redux state ====
==== React state vs. Redux state ====
Line 267: Line 276:
** We want certain form state to '''have the impact''' on the rest of our app
** We want certain form state to '''have the impact''' on the rest of our app


== Integrating React with other frameworks and plugins ==
== Integrating React with other frameworks and plugins ==
* React is '''flexible'''  
* React is '''flexible'''  
** Provides hooks that '''allow to interface''' with other libraries and frameworks
** Provides hooks that '''allow to interface''' with other libraries and frameworks
Line 276: Line 285:
** <small>https://github.com/rexxars/react-markdown</small>
** <small>https://github.com/rexxars/react-markdown</small>


=== Example 3, with ext plugin ===
=== Example 3, with ext plugin ===
<syntaxhighlight lang="ts">
<syntaxhighlight lang="ts">
// <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.js"></script>
// <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.js"></script>
Line 285: Line 294:
     super(props);
     super(props);
     this.handleChange = this.handleChange.bind(this);
     this.handleChange = this.handleChange.bind(this);
     this.state = {value: 'Type some *markdown* here!'};
     this.state = {value: 'Type some *markdown* here! # babe - meow'};
   }
   }


Line 318: Line 327:


=== Exercise 3 ===
=== Exercise 3 ===
Yup, you know what to do now, for sure (-'
# Yup, you know what to do now, for sure (-'
* You do, right? (-,
#* You do, right? (-,
* Function, obviously!
#* Function, obviously!
# Oh well, let's "kill one stone with many birds", shall we?
#* Create short training course materials: "How to kill one stone with many birds?"
#* Keep it really simple as a presentation - just a couple of slides (2 or 3)
#* Use this extension - <small>https://github.com/rexxars/react-markdown</small>
<!-- Solution
<!-- Solution
const MarkdownEditor = () => {
const MarkdownEditor = () => {
Line 348: Line 361:
-->
-->


== Bringing it all together - an application ==
== Bringing it all together - an application ==
* Using props and state usually is enough to create the whole application
* Using props and state usually is enough to create the whole application
* Below TODO list example  
* Below TODO list example  
Line 459: Line 472:
-->
-->


== Setting up your development environment ==
== Setting up your development environment ==
* Create new app
* Create new app
* Add React to an existing app
* Add React to an existing app


=== Create new app ===
=== Create new app ===
* FB incubator - the source is always the best
* FB incubator - the source is always the best, but.. it's deprecated now
** <small>https://github.com/facebook/create-react-app</small>
** <small>https://github.com/facebook/create-react-app</small>
* Common way also at the beginning is to use one of available ''starter-kits''  
** <small>https://react.dev/learn/build-a-react-app-from-scratch</small>
** <small>https://www.typescriptlang.org/docs/handbook/react-&-webpack.html</small>
* Common way also at the beginning is to use one of available ''starter-kits/fullStacks'' frameworks
** <small>https://nextjs.org/</small> (mern.io is deprecated now)
** <small>https://reactrouter.com/start/framework/installation</small>
* Some additional helpers
** <small>https://code.visualstudio.com/docs/nodejs/reactjs-tutorial</small>
** <small>https://code.visualstudio.com/docs/nodejs/reactjs-tutorial</small>
** <small>https://babeljs.io/docs/plugins/preset-react/</small>
** <small>https://babeljs.io/docs/babel-preset-react</small>
** <small>https://nextjs.org/</small> (mern.io is deprecated now)


==== Create new app Con't ====
==== Create new app Con't ====
* Later we can adjust it and move to production
* Later we can adjust it and move to production
** change the configuration
** change the configuration
Line 479: Line 494:
** wire our existing web services, write new
** wire our existing web services, write new


=== Add React to an Existing App ===
=== Add React to an Existing App ===
* No need to rewrite your app to start using React.
* No need to rewrite your app to start using React.
* Start with small part of application (small widget), and see if it works well for our use case
* Start with small part of application (small widget), and see if it works well for our use case
Line 487: Line 502:
** Package manager (Yarn, npm, etc)
** Package manager (Yarn, npm, etc)
*** Vast ecosystem of third-party packages, easy to install or update them
*** Vast ecosystem of third-party packages, easy to install or update them
** Bundler (webpack, Browserify, etc).  
** Bundler (webpack, Browserify, vite, parcel, rsbuild, etc).  
*** Lets to write modular code and bundle it together into small packages to optimize load time
*** Lets to write modular code and bundle it together into small packages to optimize load time
** Compiler (Babel, etc)
** Compiler (Babel, etc)
*** Lets to write modern JavaScript code that still works in older browsers
*** Lets to write modern JavaScript code that still works in older browsers


== Exercise 5, Full envi ==
== Exercise 5, Full envi, ts way ==
<pre>
<pre>
We will create simple application with webpack and nodejs.
We will create simple application with 'vite' and 'nodejs'.
We will do it in steps, to cover most important elements of Reactjs.
We will do it in steps, to cover most important elements of 'Reactjs'.
Each step will be explained separately.
Each step will be explained separately.


Envi preparation steps (command line, in linux you might gonna need also 'sudo ' before the 'npm')
Envi preparation steps (command line, in linux you might gonna need also 'sudo ' before the 'npm')
  npx create-react-app my-app
  npm create vite@latest mya -- --template react-ts
  cd my-app
  cd mya
  npm start
npm i
  npm run dev
</pre>
</pre>


How to use ''webpack'' with ''nodejs'' goodies
How to use ''vite'' with ''nodejs'' goodies
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
# enabling app in dev envi
# enabling app in dev envi
npm start
npm run dev
# run tests
npm test


# builds the app for production to the `build` folder
# builds the app for production to the `build` folder
Line 519: Line 532:
#* Looking at the template code
#* Looking at the template code
# Step 2
# Step 2
#* change the file ''src/App.js''<syntaxhighlight lang="ts">
#* change the file ''src/App.tsx''<syntaxhighlight lang="ts">
import './App.css';
import './App.css';


Line 532: Line 545:
export default App;
export default App;
</syntaxhighlight>
</syntaxhighlight>
# Step 3
# Step 3 (''tricky one!'')
#* Create new component here ''components/Text.js''<syntaxhighlight lang="ts">
#* Create new component here ''components/Text.tsx''<syntaxhighlight lang="ts">
const Text = () => {
const Text = () => {
     return (
     return (
Line 543: Line 556:
</syntaxhighlight>
</syntaxhighlight>
# Step 4
# Step 4
#* Modify main component ''src/App.js''<syntaxhighlight lang="ts">
#* Modify main component ''src/App.tsx''<syntaxhighlight lang="ts">
import './App.css';
import './App.css';
import Text from './components/Text';
import Text from './components/Text';
Line 558: Line 571:
export default App;
export default App;
</syntaxhighlight>
</syntaxhighlight>
# Step 5
# Step 5 <!-- (''hidden error here!'') TODO: what was it? forgot.. -->
#* Understanding state. Change main component file again ''src/App.js''<syntaxhighlight lang="ts">
#* Understanding state. Change main component file again ''src/App.tsx''<syntaxhighlight lang="ts">
import './App.css';
import './App.css';
import { React, useState } from 'react';
import { React, useState } from 'react';
Line 583: Line 596:
export default App;
export default App;
</syntaxhighlight>
</syntaxhighlight>
# Step 6
# Step 6 (couple of ''fix-me'' cases here!)
#* Passing parameters(also state) to components.  
#* Passing parameters(also state) to components.  
#** Step 6.1. Modify file ''src/components/Text.js''<syntaxhighlight lang="ts">
#** Step 6.1. Modify file ''src/components/Text.tsx''<syntaxhighlight lang="ts">
import PropTypes from 'prop-types';
import PropTypes from 'prop-types';


Line 606: Line 619:
export default Text;
export default Text;
</syntaxhighlight>
</syntaxhighlight>
#** Step 6.2. Modify main component file ''src/App.js''<syntaxhighlight lang="ts">
#** Step 6.2. Modify main component file ''src/App.tsx''<syntaxhighlight lang="ts">
import './App.css';
import './App.css';
import { React, useState } from 'react';
import { React, useState } from 'react';
Line 636: Line 649:
</syntaxhighlight>
</syntaxhighlight>


== Defining our components' parent/child relationships ==
== Defining our components' parent/child relationships ==
* Containment
* Containment
* Specialization
* Specialization
* <small>https://reactjs.org/docs/composition-vs-inheritance.html</small>
* <small>https://react.dev/learn/understanding-your-ui-as-a-tree</small>
* Legacy <small>https://reactjs.org/docs/composition-vs-inheritance.html</small>


=== Lifting state up ===
=== Lifting state up ===
Line 672: Line 686:
** the components may import it and use its constructs without extending it
** the components may import it and use its constructs without extending it


== Event handling and conditional rendering ==
== Event handling and conditional rendering ==


* Event handling
* Event handling
** <small>https://reactjs.org/docs/handling-events.html</small>
** <small>https://react.dev/learn/responding-to-events</small>
** Legacy <small>https://reactjs.org/docs/handling-events.html</small>
* Conditional rendering
* Conditional rendering
** <small>https://reactjs.org/docs/conditional-rendering.html</small>
** <small>https://react.dev/learn/conditional-rendering</small>
** Legacy <small>https://reactjs.org/docs/conditional-rendering.html</small>


== Container vs Presentational Components ==
== Container vs Presentational Components ==


  "Remember, components don’t have to emit DOM. They only need to provide composition boundaries between UI concerns"
  "Remember, components don’t have to emit DOM. They only need to provide composition boundaries between UI concerns"
Line 687: Line 703:
** it is a distinction in their purpose, not a technical one
** it is a distinction in their purpose, not a technical one
** <small>https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0</small>
** <small>https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0</small>
** use this approach only if it '''fits our business''' (processes, flow, etc)
** it can be also achieved with '''custom Hooks''' instead, so choose wisely - do not treat it as a ''golden hammer'' (-;
* Higher-Order Components
* Higher-Order Components
** <small>https://reactjs.org/docs/higher-order-components.html</small>
** rarely used in modern react code
** Legacy <small>https://reactjs.org/docs/higher-order-components.html</small>


=== Presentational Components ===
=== Presentational Components ===
Line 738: Line 757:
=== Identify the minimal representation of UI state ===
=== Identify the minimal representation of UI state ===
Criteria for each piece of data (yes = probably not state)
Criteria for each piece of data (yes = probably not state)
* Passed in from a parent via props?
* '''Passed''' in from a parent via props?
* Remain unchanged over time?
* Remain '''unchanged''' over time?
* Computable based on any other state or props in our component?
* '''Computable''' based on any other state or props in our component?


=== Identify where our state should Live ===
=== Identify where our state should Live ===
For each piece of state in our application
For each piece of state in our application
* Identify every component that renders something based on that state
* '''Identify''' every component that renders something based on that state
* Find a common owner component
* Find a '''common owner''' component
** a single component above all the components that need the state in the hierarchy
** a single component '''above''' all the components that '''need the state''' in the hierarchy
* Either the common owner or another component higher up in the hierarchy should own the state
* Either the common owner or another component '''higher up''' in the hierarchy should '''own the state'''
* If we can’t find a component where it makes sense to own the state
* If we can’t find a component where it makes sense to own the state
** create a new component simply for holding the state  
** '''create a new''' component simply for holding the state  
** and add it somewhere in the hierarchy above the common owner component
** and add it somewhere in the hierarchy '''above the common owner''' component
<!--  
<!--  
TODO: prepare an example, maybe use jewel game here?  
TODO: prepare an example, maybe use jewel game here?  
Line 757: Line 776:
* react-examples -> 03-Props-vs-State
* react-examples -> 03-Props-vs-State
-->
-->
=== Main Exercises ===
=== Main Exercises ===
# [[Express#Pesel_app|Pesel app]]
# [[Express#Pesel_app|Pesel app]]
#* Migrate from jQuery to React
#* Migrate from jQuery to React
#** do it with React functions (-----:
#** do it with React functions (-----:
#** Separate validations into JS module
#** Separate validations into JS/TS module
# [[Angular_Fundamentals#Example_1_.E2.8C.98|Jewel app]]
# [[Angular_Fundamentals#Example_1|Jewel app]]
## First migrate it from Angular to React - yes, functioooooooons ruuuleeeeezzzzzz (--;
## First migrate it from ''Angular'' to ''React'' - yes, functioooooooons ruuuleeeeezzzzzz (--;
<!--TODO: fix it
##* follow the best practices - design first, then code it (check again this [[Reactjs#Designing_our_React_app|Approach]])
<!--TODO: improve it
## Make it working and then improve it with hook ''useEffect()'' - game should start when component did mount  
## Make it working and then improve it with hook ''useEffect()'' - game should start when component did mount  
-->
-->
## Improve it with '''form''' element and extend it with '''adding new jewel''' to model
## Improve it with '''form''' element
## Extend it with '''adding new jewel''' to model
##* Add custom '''validation''' - adding same jewel not allowed
##* Add custom '''validation''' - adding same jewel not allowed
## Make sure gamers can see the '''list''' of available jewels
## Make sure gamers can see the '''list''' of available jewels
Line 854: Line 876:
-->
-->


== Handling Forms in React ==
== Handling Forms in React, classes way ==
 
Collapsed by default, old-school way here only
<div class="mw-collapsible mw-collapsed">
* Controlled vs uncontrolled inputs
* Controlled vs uncontrolled inputs
** From uncontrolled to controlled
** From uncontrolled to controlled
Line 2,413: Line 2,436:
}
}
</syntaxhighlight>
</syntaxhighlight>
 
</div>
=== Using context instead of props ===
== Using context instead of props ==
* '''Passing props''' can become '''inconvenient''' if  
* '''Passing props''' can become '''inconvenient''' if  
** we need to pass through '''many''' components
** we need to pass through '''many''' components
Line 2,427: Line 2,450:
** <small>https://react.dev/learn/managing-state#passing-data-deeply-with-context</small>
** <small>https://react.dev/learn/managing-state#passing-data-deeply-with-context</small>
* '''Exercise'''
* '''Exercise'''
** Try to re-design our ''Wizard'' form with '''context'''
** Try to re-design our ''Ftj'' form with '''context'''
*** What can you say?
*** What can you say?
*** What could be in the custom context there and what not (or shouldn't)?
*** What could be in the custom context there and what not (or shouldn't)?
Line 2,439: Line 2,462:
* Responsive routes
* Responsive routes
* <small>https://reactrouter.com/en/main</small>
* <small>https://reactrouter.com/en/main</small>
* Router framework
<syntaxhighlight lang='bash' inline>
npx create-react-router@latest my-react-router-app
</syntaxhighlight>
=== Adding Router lib ===
=== Adding Router lib ===
<syntaxhighlight lang='bash' inline>
<syntaxhighlight lang='bash' inline>
Line 2,630: Line 2,657:
* <small>https://redux.js.org/introduction</small>
* <small>https://redux.js.org/introduction</small>


=== Example and exercises ===
=== Examples and exercises ===
# ''reactExamples''_
# ''reduxExamples''_
#* ''redux-crud''
#* ''crud'', ''jwt'', ''hooks''
# ''redux-toolkit-crud'' - fix me, again (-;
# Rewrite our '''dynamic form''' example with Redux
# Rewrite our '''dynamic form''' example with Redux
# Rewrite our '''FTJ''' app with Redux
# Rewrite our '''FTJ''' app with Redux
<!-- exercise with toolkit
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import TutorialDataService from "../services/TutorialService";
const initialState = [];
export const createTutorial = createAsyncThunk(
  "tutorials/create",
  async ({ title, description }) => {
    const res = await TutorialDataService.create({ title, description });
    return res.data;
  }
);
export const retrieveTutorials = createAsyncThunk(
  "tutorials/retrieve",
  async () => {
    const res = await TutorialDataService.getAll();
    return res.data;
  }
);
export const updateTutorial = createAsyncThunk(
  "tutorials/update",
  async ({ id, data }) => {
    const res = await TutorialDataService.update(id, data);
    return res.data;
  }
);
//// Moriarty's back - agaaaaaaaaain!
// - deleting one tutorial is gone, fix it!
//
export const deleteAllTutorials = createAsyncThunk(
  "tutorials/deleteAll",
  async () => {
    const res = await TutorialDataService.removeAll();
    return res.data;
  }
);
export const findTutorialsByTitle = createAsyncThunk(
  "tutorials/findByTitle",
  async ({ title }) => {
    const res = await TutorialDataService.findByTitle(title);
    return res.data;
  }
);
const tutorialSlice = createSlice({
  name: "tutorial",
  initialState,
  extraReducers: {
    [createTutorial.fulfilled]: (state, action) => {
      state.push(action.payload);
    },
    [retrieveTutorials.fulfilled]: (state, action) => {
      return [...action.payload];
    },
    [updateTutorial.fulfilled]: (state, action) => {
      const index = state.findIndex(tutorial => tutorial.id === action.payload.id);
      state[index] = {
        ...state[index],
        ...action.payload,
      };
    },
    [deleteAllTutorials.fulfilled]: (state, action) => {
      return [];
    },
    [findTutorialsByTitle.fulfilled]: (state, action) => {
      return [...action.payload];
    },
  },
});
const { reducer } = tutorialSlice;
export default reducer;
-->


== Managing state with Mobx ==
== Managing state with Mobx ==
Line 2,692: Line 2,805:
* Rewrite our dynamic form example with Mobx
* Rewrite our dynamic form example with Mobx
** Use this boilerplate <small>https://github.com/mobxjs/mobx-react-boilerplate</small>
** Use this boilerplate <small>https://github.com/mobxjs/mobx-react-boilerplate</small>
 
** TS version <small>https://github.com/mobxjs/mobx-react-typescript-boilerplate</small>
<!-- TODO: finish this topic (shortly)
<!-- TODO: finish this topic (shortly)
== Flux way ==
== Flux way ==
Line 2,719: Line 2,832:
* Examples
* Examples
** <small>https://react.dev/reference/react/useEffect</small>
** <small>https://react.dev/reference/react/useEffect</small>
=== Hooks Examples ===
* ''useState()''
* ''useMemo()''
* ''useId()''
* ''useCallback()''
* ''useEffect()''
* ''useRef()''
* ''useContext()''
<!-- TODO: make this better
<!-- TODO: make this better
=== Hooks Examples ===
* ''other_/fe_''
* ''other_/fe_''


Line 2,728: Line 2,849:
*# Missing functionality - removing single content
*# Missing functionality - removing single content
-->
-->
<br/>


== React with Typescript ==
==== useState() ====
* [[Typescript|Typescript basics]]
* lets to add a '''state variable''' to our component which '''returns an array with two values'''
* From react docs
** ''Current State''
** <small>https://reactjs.org/docs/static-type-checking.html#typescript</small>
** ''Set Function''
* From ts docs
* we can pass on initial value as well like below:- 234 and 'Gulliver'
** <small>https://www.typescriptlang.org/docs/handbook/jsx.html</small>
<syntaxhighlight lang="ts">
* Best practices, use-cases, etc
import { useState } from 'react';
** <small>https://github.com/typescript-cheatsheets/react-typescript-cheatsheet</small>
* Examples
** ''fe_/react-typescript..''  
* Exercises
** Create new app with ts included
*** <syntaxhighlight inline lang="bash">npx create-react-app my-app-ts --template typescript</syntaxhighlight>
** Rewrite our PESEL app with TS
** Rewrite our FTJ
<!-- TODO: fix that one below or add better exercises
** '''todo_ts''' app
** Exercise - add ''"Remove All"'' button
-->


== React Native ==
function Invoice() {
* Creates native apps for '''Android''', '''iOS'''
  const [invNo, setInvNo] = useState(234);
* '''Written''' in JavaScript — '''rendered''' with native code
  const [companyName, setCompanyName] = useState('Gulliver');
* Seamless '''Cross-Platform'''
}
** React components '''wrap''' existing native code
</syntaxhighlight>
** interact with native APIs via React’s declarative UI paradigm and JavaScript
* Setup and Using
** '''Expo Go''' - quick and easy, but restricted SDK libs
** '''Native CLI''' - longer to prepare, but gives access to many external libs
* Related docs, examples
** <small>https://reactnative.dev/docs/environment-setup</small>
** <small>https://snack.expo.dev/?platform=web</small>


=== React Native Con't ===
==== useMemo() ====
npx create-expo-app nativeExample
* lets to '''cache''' the result of a '''calculation between re-renders'''
  cd nativeExample
** '''prevents''' the unnecessary '''renders''' in our React Application
npx expo start
<syntaxhighlight lang="ts">
import { useMemo } from 'react';
 
function InvoicesList({ invoices, tab }) {
  const visibleInvoices = useMemo(
  () => filterInvoices(invoices, tab),
  [invoices, tab]
  );
}
</syntaxhighlight>
 
==== useId() ====
* for '''generating unique IDs''' that can be passed to '''accessibility attributes'''
** which let to specify that '''two tags are related''' to each other
** where we can use useId() generated ids instead of hardcoding them
<syntaxhighlight lang="ts">
import { useId } from 'react';
 
function PasswordField() {
  const passwordClueId = useId();
 
  return (
  <>
    <input type="password" aria-describedby={passwordClueId} />
    <p id={passwordClueId}>
  </>
  )
}
</syntaxhighlight>
 
==== useCallback() ====
* lets to '''cache a function''' definition between '''re-renders'''
* ''useCallback'' caches a 'function' and ''useMemo'' caches a 'value/result of a calculation'
<syntaxhighlight lang="ts">
import { useCallback } fom 'react';
 
export default function BookingPage({ bookingId, referrer, theme }) {
  const handleSubmit = useCallback((leadDetails) => {
  post('/booking/' + bookingId+ '/buy' {
    referrer,
    leadDetails,
    });
  }, [bookingId, referrer]);
(...)
</syntaxhighlight>
 
==== useEffect() ====
* lets to perform side-effects in the component
* side effects basically is an '''action''' which '''connects''' the component to the '''outside''' world
<syntaxhighlight lang="ts">
(...)
useEffect(() => {
  const connection = createConnection(serverCourseEventUrl, courseEventRoomId);
  connection.connect();
  return () => {
  connection.disconnect();
  };
  }, [serverCourseEventUrl, courseEventRoomId]);
(...)
</syntaxhighlight>
 
==== useRef() ====
* lets to '''reference''' a value that's '''not needed''' for rendering
* similar to ''useState'', the difference is it '''doesn't cause a re-render''' when the value changes
<syntaxhighlight lang="ts">
import { useRef } from 'react';
 
function RefreshDadesktopSandbox() {
  const intervalRef = useRef(0);
  const inputRef = useRef(null);
(...)
}
</syntaxhighlight>


Later on
==== useContext() ====
* connect on the same Wifi with phone
* lets to '''read and subscribe to context''' from our component
* install Expo Go app on phone
* just like a data store (Redux, Mobx, etc)
* scan the QR
<syntaxhighlight lang="ts">
import { useContext } from 'react';
import { ThemeLeadContext} from './ThemeLeadContext.js';


== Full stack with React ==
function SkinForLead() {
* Examples
  const theme = useContext(ThemeLeadContext);
(...)
  return (
    <section className="np-lead-section">
      <ThemeLeadContext value={leadType + 1}>
        {children}
      </ThemeLeadContext>
    </section>
  );
}
</syntaxhighlight>
 
=== Custom Hook ===
We can define our '''own hooks''' to use state and other React features without writing a class
* As a function, it takes '''input''' and returns '''output'''
* name starts with '''use''' (''useQuery, useMedia..'', etc)
* returns a normal, '''non-jsx data''' (not like in functional component)
* can use '''other hooks''' such as useState, useReF.. and '''other custom hooks''' (-;
* some '''libraries''' also provide hooks - '''useForm''' (''React Hook Form''), '''useMediaQuery''' (''MUI''), '''useQuery''' (''React Query''), etc
 
=== Custom Hook Con't ===
'''Benefits:'''
* Completely '''separate''' logic from user interface
* '''Reusable''' in many different components with the same processing logic
** Therefore, the logic only needs to be '''fixed in one place''' if it changes
* '''Share''' logic between components
* '''Hide''' code with complex logic in a component, make the component '''easier''' to read
'''When to use:'''
* piece of code (logic) is '''reused''' in many places
<!--
it’s easy to see when you copy a whole piece of code without editing anything, except for the parameter passed. Split like how you separate a function
-->
* logic is '''too long''' and complicated
<!--
you want to write it in another file, so that your component is shorter and easier to read because you don’t need to care about the logic of that hook anymore
-->
* we write '''custom''' react library (-:
* ... ? (your ideas, please!)
* '''''Example''''' - let's look at '''useNavigate()''' in ''react-router'' lib
* '''''Example/Exercise''''' - ''react-ts-api''
<br/>
<!-- TODO: prep better exercise here (custom hook)
import { useState, useEffect } from "react";
import axios, { AxiosRequestConfig } from "axios";
 
 
//// Setup 'AxiosRequestConfig'
 
 
 
 
 
//// Your hook definition (-;
// - should use 'AxiosRequestConfig' as argument
// - should fetch asynchronously tutorials
// --- should handle data, error, loading (for now implement only 'data' and 'error' and default loading to false)
// - later on make sure our components will use it instead of 'service'
// - hints: use 'useState', 'useEffect'
 
 
 
 
 
 
-->
 
== React Query ==
* Most state management libraries (incl ''Redux'')
** good for working with '''client state'''
** not for server state
*** persists remotely in a location the client side cannot control
*** can become outdated - we need to make asynchronous APIs for fetching and updating
* React Query - one of the best libraries for '''managing server state'''
** It '''helps''' to fetch, cache, synchronize and update data '''without touching any global state'''
 
=== React Query Con't ===
Helps:
* to '''remove complicated and misunderstood code''' and replace with several React Query logic
* '''easier to maintain and build new features''' without worrying about wiring up new server state data sources
* make our application feel '''faster and more responsive'''
* save bandwidth and increase memory '''performance'''
* '''''Example/Exercise''''' - ''reactq-axios-ts''
<br />
<!-- Exercise
import React, { useState, useEffect } from "react";
import { useQuery, useMutation } from "react-query";
import "./App.css";
 
import Tutorial from "./types/Tutorial"
import TutorialService from "./services/TutorialService"
 
const App: React.FC = () => {
  const [getId, setGetId] = useState("");
  const [getTitle, setGetTitle] = useState("");
 
  const [postTitle, setPostTitle] = useState("");
  const [postDescription, setPostDescription] = useState("");
 
  const [putId, setPutId] = useState("");
  const [putTitle, setPutTitle] = useState("");
  const [putDescription, setPutDescription] = useState("");
  const [putPublished, setPutPublished] = useState(false);
 
  const [deleteId, setDeleteId] = useState("");
 
  const [getResult, setGetResult] = useState<string | null>(null);
  const [postResult, setPostResult] = useState<string | null>(null);
  const [putResult, setPutResult] = useState<string | null>(null);
  const [deleteResult, setDeleteResult] = useState<string | null>(null);
 
  const fortmatResponse = (res: any) => {
    return JSON.stringify(res, null, 2);
  };
 
  const { isLoading: isLoadingTutorials, refetch: getAllTutorials } = useQuery<Tutorial[], Error>(
    "query-tutorials",
    async () => {
      return await TutorialService.findAll();
    },
    {
      enabled: false,
      onSuccess: (res) => {
        setGetResult(fortmatResponse(res));
      },
      onError: (err: any) => {
        setGetResult(fortmatResponse(err.response?.data || err));
      },
    }
  );
 
  useEffect(() => {
    if (isLoadingTutorials) setGetResult("loading...");
  }, [isLoadingTutorials]);
 
  function getAllData() {
    try {
      getAllTutorials();
    } catch (err) {
      setGetResult(fortmatResponse(err));
    }
  }
 
  const { isLoading: isLoadingTutorial, refetch: getTutorialById } = useQuery<Tutorial, Error>(
    "query-tutorial-by-id",
    async () => {
      return await TutorialService.findById(getId);
    },
    {
      enabled: false,
      retry: 1,
      onSuccess: (res) => {
        setGetResult(fortmatResponse(res));
      },
      onError: (err: any) => {
        setGetResult(fortmatResponse(err.response?.data || err));
      },
    }
  );
 
  useEffect(() => {
    if (isLoadingTutorial) setGetResult("loading...");
  }, [isLoadingTutorial]);
 
  function getDataById() {
    if (getId) {
      try {
        getTutorialById();
      } catch (err) {
        setGetResult(fortmatResponse(err));
      }
    }
  }
 
  const { isLoading: isSearchingTutorial, refetch: findTutorialsByTitle } = useQuery<Tutorial[], Error>(
    "query-tutorials-by-title", // ["query-tutorials-by-title", getTitle],
    async () => {
      return await TutorialService.findByTitle(getTitle);
    },
    {
      enabled: false,
      retry: 1,
      onSuccess: (res) => {
        setGetResult(fortmatResponse(res));
      },
      onError: (err: any) => {
        setGetResult(fortmatResponse(err.response?.data || err));
      },
    }
  );
 
  useEffect(() => {
    if (isSearchingTutorial) setGetResult("searching...");
  }, [isSearchingTutorial]);
 
  function getDataByTitle() {
    if (getTitle) {
      try {
        findTutorialsByTitle();
      } catch (err) {
        setGetResult(fortmatResponse(err));
      }
    }
  }
 
  const { isLoading: isPostingTutorial, mutate: postTutorial } = useMutation<any, Error>(
    async () => {
      return await TutorialService.create(
        {
          title: postTitle,
          description: postDescription
        });
    },
    {
      onSuccess: (res) => {
        setPostResult(fortmatResponse(res));
      },
      onError: (err: any) => {
        setPostResult(fortmatResponse(err.response?.data || err));
      },
    }
  );
 
  useEffect(() => {
    if (isPostingTutorial) setPostResult("posting...");
  }, [isPostingTutorial]);
 
  function postData() {
    try {
      postTutorial();
    } catch (err) {
      setPostResult(fortmatResponse(err));
    }
  }
 
  const { isLoading: isUpdatingTutorial, mutate: updateTutorial } = useMutation<any, Error>(
    async () => {
      return await TutorialService.update(
        putId,
        {
          title: putTitle,
          description: putDescription,
          published: putPublished
        });
    },
    {
      onSuccess: (res) => {
        setPutResult(fortmatResponse(res));
      },
      onError: (err: any) => {
        setPutResult(fortmatResponse(err.response?.data || err));
      },
    }
  );
 
  useEffect(() => {
    if (isUpdatingTutorial) setPutResult("updating...");
  }, [isUpdatingTutorial]);
 
  function putData() {
    if (putId) {
      try {
        updateTutorial();
      } catch (err) {
        setPutResult(fortmatResponse(err));
      }
    }
  }
 
  const { isLoading: isDeletingTutorials, mutate: deleteAllTutorials } = useMutation<any, Error>(
    async () => {
      return await TutorialService.deleteAll();
    },
    {
      onSuccess: (res) => {
        setDeleteResult(fortmatResponse(res));
      },
      onError: (err: any) => {
        setDeleteResult(fortmatResponse(err.response?.data || err));
      },
    }
  );
 
  useEffect(() => {
    if (isDeletingTutorials) setDeleteResult("deleting...");
  }, [isDeletingTutorials]);
 
  function deleteAllData() {
    try {
      deleteAllTutorials();
    } catch (err) {
      setDeleteResult(fortmatResponse(err));
    }
  }
 
  //// Moriarty's back! Ha Ha ha haaaaaaaaaaaa !!!
  // 1. mutate deletion of one 'tutorial'
  // 2. 'effect' it (-;
  // 3. add missing handler
 
 
 
 
 
 
 
 
 
 
 
 
  const clearGetOutput = () => {
    setGetResult(null);
  };
 
  const clearPostOutput = () => {
    setPostResult(null);
  };
 
  const clearPutOutput = () => {
    setPutResult(null);
  };
 
  const clearDeleteOutput = () => {
    setDeleteResult(null);
  };
 
  return (
    <div id="app" className="container my-3">
      <h3>React Query Axios Typescript example</h3>
 
      <div className="card mt-3">
        <div className="card-header">React Query Axios Typescript GET - BezKoder.com</div>
        <div className="card-body">
          <div className="input-group input-group-sm">
            <button className="btn btn-sm btn-primary" onClick={getAllData}>
              Get All
            </button>
 
            <input
              type="text"
              value={getId}
              onChange={(e) => setGetId(e.target.value)}
              className="form-control ml-2"
              placeholder="Id"
            />
            <div className="input-group-append">
              <button className="btn btn-sm btn-primary" onClick={getDataById}>
                Get by Id
              </button>
            </div>
 
            <input
              type="text"
              value={getTitle}
              onChange={(e) => setGetTitle(e.target.value)}
              className="form-control ml-2"
              placeholder="Title"
            />
            <div className="input-group-append">
              <button
                className="btn btn-sm btn-primary"
                onClick={getDataByTitle}
              >
                Find By Title
              </button>
            </div>
 
            <button
              className="btn btn-sm btn-warning ml-2"
              onClick={clearGetOutput}
            >
              Clear
            </button>
          </div>
 
          {getResult && (
            <div className="alert alert-secondary mt-2" role="alert">
              <pre>{getResult}</pre>
            </div>
          )}
        </div>
      </div>
 
      <div className="card mt-3">
        <div className="card-header">React Query Axios Typescript POST - BezKoder.com</div>
        <div className="card-body">
          <div className="form-group">
            <input
              type="text"
              value={postTitle}
              onChange={(e) => setPostTitle(e.target.value)}
              className="form-control"
              placeholder="Title"
            />
          </div>
          <div className="form-group">
            <input
              type="text"
              value={postDescription}
              onChange={(e) => setPostDescription(e.target.value)}
              className="form-control"
              placeholder="Description"
            />
          </div>
          <button className="btn btn-sm btn-primary" onClick={postData}>
            Post Data
          </button>
          <button
            className="btn btn-sm btn-warning ml-2"
            onClick={clearPostOutput}
          >
            Clear
          </button>
 
          {postResult && (
            <div className="alert alert-secondary mt-2" role="alert">
              <pre>{postResult}</pre>
            </div>
          )}
        </div>
      </div>
 
      <div className="card mt-3">
        <div className="card-header">React Query Axios Typescript PUT - BezKoder.com</div>
        <div className="card-body">
          <div className="form-group">
            <input
              type="text"
              value={putId}
              onChange={(e) => setPutId(e.target.value)}
              className="form-control"
              placeholder="Id"
            />
          </div>
          <div className="form-group">
            <input
              type="text"
              value={putTitle}
              onChange={(e) => setPutTitle(e.target.value)}
              className="form-control"
              placeholder="Title"
            />
          </div>
          <div className="form-group">
            <input
              type="text"
              value={putDescription}
              onChange={(e) => setPutDescription(e.target.value)}
              className="form-control"
              placeholder="Description"
            />
          </div>
          <div className="form-check mb-2">
            <input
              type="checkbox"
              name="putPublished"
              checked={putPublished}
              onChange={(e) => setPutPublished(e.target.checked)}
              className="form-check-input"
            />
            <label className="form-check-label" htmlFor="putPublished">
              Publish
            </label>
          </div>
          <button className="btn btn-sm btn-primary" onClick={putData}>
            Update Data
          </button>
          <button
            className="btn btn-sm btn-warning ml-2"
            onClick={clearPutOutput}
          >
            Clear
          </button>
 
          {putResult && (
            <div className="alert alert-secondary mt-2" role="alert">
              <pre>{putResult}</pre>
            </div>
          )}
        </div>
      </div>
 
      <div className="card mt-3">
        <div className="card-header">
          React Query Axios Typescript DELETE - BezKoder.com
        </div>
        <div className="card-body">
          <div className="input-group input-group-sm">
            <button className="btn btn-sm btn-danger" onClick={deleteAllData}>
              Delete All
            </button>
 
            <input
              type="text"
              value={deleteId}
              onChange={(e) => setDeleteId(e.target.value)}
              className="form-control ml-2"
              placeholder="Id"
            />
            <div className="input-group-append">
              <button
                className="btn btn-sm btn-danger"
                onClick={deleteDataById}
              >
                Delete by Id
              </button>
            </div>
 
            <button
              className="btn btn-sm btn-warning ml-2"
              onClick={clearDeleteOutput}
            >
              Clear
            </button>
          </div>
 
          {deleteResult && (
            <div className="alert alert-secondary mt-2" role="alert">
              <pre>{deleteResult}</pre>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
 
export default App;
-->
 
== React Hook Form ==
* '''Less code''', more performant
** reduces the amount of code we need to write
** removes unnecessary re-renders
* '''Isolate''' re-renders
** ability to isolate component re-renders -> better performance on our page or app
* '''Subscriptions''' - better performance
** ability to '''subscribe to individual input''' and form State update without re-rendering the entire form
* Faster '''mounting''' (than Formik, ReduxForm, etc)
* More here <small>https://react-hook-form.com/</small>
* '''''Example/Exercise''''' - ''react-hook-form-ts''
<br />
<!-- Exercise
import './App.css';
import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';
 
type UserSubmitForm = {
  fullname: string;
  username: string;
  email: string;
  password: string;
  confirmPassword: string;
};
 
const App: React.FC = () => {
  const validationSchema = Yup.object().shape({
    fullname: Yup.string().required('Fullname is required'),
    username: Yup.string()
      .required('Username is required')
      .min(6, 'Username must be at least 6 characters')
      .max(20, 'Username must not exceed 20 characters'),
    email: Yup.string()
      .required('Email is required')
      .email('Email is invalid'),
    password: Yup.string()
      .required('Password is required')
      .min(6, 'Password must be at least 6 characters')
      .max(40, 'Password must not exceed 40 characters'),
    confirmPassword: Yup.string()
      .required('Confirm Password is required')
      .oneOf([Yup.ref('password'), ''], 'Confirm Password does not match')
  });
 
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors }
  } = useForm<UserSubmitForm>({
    resolver: yupResolver(validationSchema)
  });
 
  const onSubmit = (data: UserSubmitForm) => {
    console.log(JSON.stringify(data, null, 2));
  };
 
  return (
    <div className="register-form">
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="form-group">
          <label>Full Name</label>
          <input
            type="text"
            {...register('fullname')}
            className={`form-control ${errors.fullname ? 'is-invalid' : ''}`}
          />
          <div className="invalid-feedback">{errors.fullname?.message}</div>
        </div>
 
        <div className="form-group">
          <label>Username</label>
          <input
            type="text"
            {...register('username')}
            className={`form-control ${errors.username ? 'is-invalid' : ''}`}
          />
          <div className="invalid-feedback">{errors.username?.message}</div>
        </div>
 
        <div className="form-group">
          <label>Email</label>
          <input
            type="text"
            {...register('email')}
            className={`form-control ${errors.email ? 'is-invalid' : ''}`}
          />
          <div className="invalid-feedback">{errors.email?.message}</div>
        </div>
 
        <div className="form-group">
          <label>Password</label>
          <input
            type="password"
            {...register('password')}
            className={`form-control ${errors.password ? 'is-invalid' : ''}`}
          />
          <div className="invalid-feedback">{errors.password?.message}</div>
        </div>
        <div className="form-group">
          <label>Confirm Password</label>
          <input
            type="password"
            {...register('confirmPassword')}
            className={`form-control ${
              errors.confirmPassword ? 'is-invalid' : ''
            }`}
          />
          <div className="invalid-feedback">
            {errors.confirmPassword?.message}
          </div>
        </div>
 
        <div className="form-group">
          <button type="submit" className="btn btn-primary">
            Register
          </button>
          <button
            type="button"
            onClick={() => reset()}
            className="btn btn-warning float-right"
          >
            Reset
          </button>
        </div>
      </form>
    </div>
  );
};
 
export default App;
-->
 
== MUI ==
* '''MUI System''' - CSS utilities
** <small>https://mui.com/system/getting-started/</small>
* '''BaseUI''' - blank canvas
** <small>https://mui.com/base-ui/</small>
* '''MaterialUI''' - Google's Material Design
** <small>https://mui.com/material-ui/</small>
* '''JoyUI''' - MUI's own design
** <small>https://mui.com/joy-ui/getting-started/</small>
* ''MUI X'' - free + paid pro-options
** <small>https://mui.com/x/react-data-grid/demo/</small>
* ''Store'' with '''paid''' goodies
** <small>https://mui.com/store/</small>
* Good ''alternative'' - <small>https://chakra-ui.com/</small>
 
=== MUI System ===
* Set of '''utilities''' (''Flexbox, Shadows, Typography, etc'')
** for quickly '''applying styles''' to components from: ''BUI, MUI, JUI''
** or to any '''third-party''' components
* Helps to '''build''' custom designs more '''efficiently'''
** and to '''rapidly lay out''' custom designs
* Set of flexible, generic '''wrapper''' components (''Box, Container, Grid, Stack'')
** can be quickly customized using the <big>'''sx'''</big> ''prop''
*** styles can be '''defined directly''' within the components themselves
*** '''simplifies''' bulky and redundant definitions of ''styled-components''
*** ensures '''consistency''' in '''one-off''' styles too
* ''CSS tricks'' - <small>https://css-tricks.com/snippets/css/a-guide-to-flexbox/</small>
 
=== BaseUI ===
* '''Library''' of "unstyled" ''React'' components (standalone) and '''low-level''' hooks
* Set of foundational '''"headless"''' components
** can be built with using '''any styling''' solution
** no need to '''override''' any default style engine or theme
*  "skeletal" version of ''Material UI''
 
=== MaterialUI ===
* Uses this under the bonnet - <small>https://m2.material.io/</small>
* '''Comprehensive''' and can be used '''in production''' out of the box
** comes packaged with '''default styles'''
** is '''optimized''' to work with
*** '''Emotion''' (default CSS engine, in v6) - <small>https://emotion.sh/docs/introduction</small>
*** or '''styled-components''' - <small>https://styled-components.com/</small>
* ''Templates'' - <small>https://mui.com/material-ui/getting-started/templates/</small>
** Customizing - <small>https://mui.com/material-ui/customization/how-to-customize/</small>
*** Theming - <small>https://mui.com/material-ui/customization/theming/</small>
*** Tools/Builders
**** '''Theme Creator''' - <small>https://zenoo.github.io/mui-theme-creator/</small>
**** '''Color Palette Generator''' - <small>https://m2.material.io/inline-tools/color/</small>
* '''''Example''''' - ''material-ui-vite-ts''
 
=== MaterialUI Exercise ===
# Here choose the '''environment'''
#* <small>https://mui.com/material-ui/getting-started/example-projects/</small>
#* we will use that one
#** <small>https://github.com/mui/material-ui/tree/master/examples/material-ui-vite-ts</small>
# Next decide about the '''template''' (or not)
#* <small>https://mui.com/material-ui/getting-started/templates/</small>
#* let's take this one - ''Blog''
#** <small>https://github.com/mui/material-ui/tree/v6.1.1/docs/data/material/getting-started/templates/blog</small>
#** make it working
# Finally try to use this '''builder'''
#* <small>https://zenoo.github.io/mui-theme-creator/</small>
#** create custom theme settings with it - apply them
 
=== JoyUI ===
* List Example - <small>https://mui.com/joy-ui/react-list/</small>
* Template Example - <small>https://mui.com/joy-ui/getting-started/templates/order-dashboard/</small>
 
== React with Typescript ==
* [[Typescript|Typescript basics]]
* From react docs
** <small>https://react.dev/learn/typescript</small>
** Legacy: <small>https://reactjs.org/docs/static-type-checking.html#typescript</small>
* From ts docs
** <small>https://www.typescriptlang.org/docs/handbook/jsx.html</small>
* Best practices, use-cases, etc
** <small>https://github.com/typescript-cheatsheets/react-typescript-cheatsheet</small>
* Examples
** ''fe_/react-ts..''
* Exercises
** Create new app with ts included
*** <syntaxhighlight inline lang="bash">npx create-react-app my-app-ts --template typescript</syntaxhighlight> (''depricated'')
*** <syntaxhighlight inline lang="bash">npm create vite@latest mya -- --template react-ts</syntaxhighlight>
** Rewrite our PESEL app with TS
** Rewrite our FTJ
<!-- TODO: fix that one below or add better exercises
** '''todo_ts''' app
** Exercise - add ''"Remove All"'' button
-->
 
== React Native ==
* Creates native apps for '''Android''', '''iOS'''
* '''Written''' in JavaScript — '''rendered''' with native code
* Seamless '''Cross-Platform'''
** React components '''wrap''' existing native code
** interact with native APIs via React’s declarative UI paradigm and JavaScript
* Setup and Using
** '''Expo Go''' - quick and easy, but restricted SDK libs
** '''Native CLI''' - longer to prepare, but gives access to many external libs
* Related docs, examples
** <small>https://reactnative.dev/docs/environment-setup</small>
** <small>https://snack.expo.dev/?platform=web</small>
 
=== React Native Con't ===
npx create-expo-app nativeExample
cd nativeExample
npx expo start
 
Later on
* connect on the same Wifi with phone
* install Expo Go app on phone
* scan the QR
 
== Full stack with React ==
* Examples
** <small>https://nextjs.org/ (mern.io deprecated..)</small>
** <small>https://nextjs.org/ (mern.io deprecated..)</small>
* Installing - <small>https://nextjs.org/docs/app/getting-started/installation</small>
* '''Exercises'''
** Migrate '''ftj+pesel''' into ''nextjs''
npx create-next-app@latest ftjrn
cd ftjrn
npm run dev
<!-- npx create-next-app@latest --example with-xstate ftjrn -->
=== nextjs with xstate ===
<syntaxhighlight lang="bash">
npx create-next-app@latest --example with-xstate myax
</syntaxhighlight>
=== nextjs with hook-form ===
<syntaxhighlight lang="bash">
npx create-next-app --example with-react-hook-form myahf
</syntaxhighlight>
=== nextjs with forms ===
<syntaxhighlight lang="bash">
npx create-next-app --example next-forms myaf
</syntaxhighlight>
=== nextjs with mongodb and tailwind ===
''SIMPLE'' - with '''tailwind''' css
<syntaxhighlight lang="bash">
npx create-next-app --example with-mongodb myam
</syntaxhighlight>
''STRONGER'' - with '''crud''' actions
<syntaxhighlight lang="bash">
npx create-next-app --example with-mongodb-mongoose myams
</syntaxhighlight>


== Server side rendering and SEO ==
== Server side rendering and SEO ==
Line 2,816: Line 3,803:
== Debugging ==
== Debugging ==
* Devtools
* Devtools
** <small>https://fb.me/react-devtools</small>
** <small>https://react.dev/learn/react-developer-tools</small>
** <small>https://github.com/reduxjs/redux-devtools</small>
** <small>https://github.com/reduxjs/redux-devtools</small>
* Error Boundaries
* Error Boundaries
** <small>https://reactjs.org/docs/error-boundaries.html</small>
** <small>https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary</small>
** Legacy <small>https://reactjs.org/docs/error-boundaries.html</small>


== Upgrading, Refactoring ==
== Upgrading, Refactoring ==
Line 2,834: Line 3,822:
** <small>https://www.npmjs.com/package/yup</small>
** <small>https://www.npmjs.com/package/yup</small>


== Testing your React web application ==
== Testing your React web application ==
* '''Jest'''
* '''Jest'''
** <small>https://github.com/Microsoft/TypeScript-React-Starter#typescript-react-starter</small> (depricated )
** <small>https://github.com/Microsoft/TypeScript-React-Starter#typescript-react-starter</small> (depricated )
Line 2,841: Line 3,829:
[[File:JestGeneric.jpg]]
[[File:JestGeneric.jpg]]


=== Jest Intro ===
=== Jest Intro ===
* No config
* No config
<!-- aims to work out of the box, config free, on most JavaScript projects -->
<!-- aims to work out of the box, config free, on most JavaScript projects -->
Line 2,852: Line 3,840:
<!-- From it to expect - Jest has the entire toolkit in one place. Well documented, well maintained, well good -->
<!-- From it to expect - Jest has the entire toolkit in one place. Well documented, well maintained, well good -->


=== Overview of Jest ===
=== Overview of Jest ===
* Zippy, secure
* Zippy, secure
** tests have '''unique global state'''
** tests have '''unique global state'''
Line 2,859: Line 3,847:
** '''re-organizes runs''' based on how long test files take
** '''re-organizes runs''' based on how long test files take


=== Overview Con't ===
=== Overview Con't ===
* Code '''coverage''' (''--coverage'')
* Code '''coverage''' (''--coverage'')
** No setup needed, collects from entire projects (incl untested files)
** No setup needed, collects from entire projects (incl untested files)
Line 2,866: Line 3,854:
** rich '''Mock Functions API''' with readable syntax
** rich '''Mock Functions API''' with readable syntax


=== Overview ===
=== Overview ===
* Whacking exceptions
* Whacking exceptions
** rich context for '''test-fail''': ''toStrictEqual, toHaveProperty, toMatchSnapshoot, toThrowError'', etc
** rich context for '''test-fail''': ''toStrictEqual, toHaveProperty, toMatchSnapshoot, toThrowError'', etc
Line 2,874: Line 3,862:
* Extendable
* Extendable


=== Benefits of testing ===
=== Benefits of testing ===
'''Why and how'''
'''Why and how'''
* Interfaces depend on '''browsers''', user '''interactions''', and many other variables  
* Interfaces depend on '''browsers''', user '''interactions''', and many other variables  
Line 2,886: Line 3,874:
* Running tests using ''Node.js'' and the console <big>'''==>'''</big> faster, more predictable
* Running tests using ''Node.js'' and the console <big>'''==>'''</big> faster, more predictable


=== Benefits of testing Con't ===
=== Benefits of testing Con't ===
* Is new feature affecting other parts of the application
* Is new feature affecting other parts of the application
** Tests '''avoid''' regressions <big>'''==>'''</big> tell if the new code breaks the old tests
** Tests '''avoid''' regressions <big>'''==>'''</big> tell if the new code breaks the old tests
Line 2,898: Line 3,886:
*** clicking a button <big>'''==>'''</big> tests to check all the related event handlers  
*** clicking a button <big>'''==>'''</big> tests to check all the related event handlers  


=== Benefits of testing Con't - tdd, design ===
=== Benefits of testing Con't - tdd, design ===
* Tests can verify the component's behavior on '''edge cases'''
* Tests can verify the component's behavior on '''edge cases'''
** all the props are '''null''', or there is an '''error'''
** all the props are '''null''', or there is an '''error'''
Line 2,906: Line 3,894:
** Force to think more about the '''design''' before implementing <big>'''==>'''</big> usually leads to higher quality
** Force to think more about the '''design''' before implementing <big>'''==>'''</big> usually leads to higher quality


=== Setting up the Testing Environment ===
=== Setting up the Testing Environment ===
* Examples
* Examples
** Folder ''"testing"''
** Folder ''"testing"''
Line 2,914: Line 3,902:
* Cypress - e2e framework
* Cypress - e2e framework


==== Installing and Configuring Jest ====
==== Installing and Configuring Jest ====
* Initial setup
* Initial setup
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
Line 2,928: Line 3,916:
** <small>jestjs.io/docs/getting-started#additional-configuration</small>
** <small>jestjs.io/docs/getting-started#additional-configuration</small>


==== Running Watch Mode to Test File Changes ====
==== Running Watch Mode to Test File Changes ====
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
live-server --cors #manual tests with real browser
live-server --cors #manual tests with real browser
Line 2,947: Line 3,935:
More here: <small>jestjs.io/docs/cli</small>
More here: <small>jestjs.io/docs/cli</small>


==== Running Browser Tests through Node ====
==== Running Browser Tests through Node ====
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
npm test  #via 'create-react-app' it uses by default the extension called 'react-scripts'
npm test  #via 'create-react-app' it uses by default the extension called 'react-scripts'
</syntaxhighlight>
</syntaxhighlight>


=== Testing a Sample JavaScript Application ===
=== Testing a Sample JavaScript Application ===
'''TDD''' - Test Driven Development
'''TDD''' - Test Driven Development
* '''Red-Green-Refactor''' cycle
* '''Red-Green-Refactor''' cycle
Line 2,967: Line 3,955:
*** For example '''create-react-app''' application template - favicon.ico, a sample logo, CSS files, etc
*** For example '''create-react-app''' application template - favicon.ico, a sample logo, CSS files, etc


==== TDD Con't ====
==== TDD Con't ====
[[File:TddJestReact.png|600px]]
[[File:TddJestReact.png|600px]]


==== Good vs Great ====
==== Good vs Great ====
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 2,984: Line 3,972:
|}
|}


==== Examples ====
==== Examples ====
* Example 1 - ''"1oneDetailOnly"''
* Example 1 - ''"1oneDetailOnly"''
* Example 2 - ''"2viewsListAndDetail"''
* Example 2 - ''"2viewsListAndDetail"''
Line 2,990: Line 3,978:
[[File:JewelsListViewDesign.png|500px]]
[[File:JewelsListViewDesign.png|500px]]


==== Exercises ====
==== Exercises ====
# (''"ex1"'') Display in the UI the rest of fields from model
# (''"ex1"'') Display in the UI the rest of fields from model
#* Wrap data into HTML table
#* Wrap data into HTML table
Line 3,001: Line 3,989:
#** Clicked jewel button should be different then the rest of jewel buttons (add css class dynamically)
#** Clicked jewel button should be different then the rest of jewel buttons (add css class dynamically)


=== Testing a React App ===
=== Testing a React App ===
* Simple form and user input
* Simple form and user input
* Testing React Components
* Testing React Components
* About Stateful Components
* About Stateful Components


==== Set up, execute function, assert results ====
==== Set up, execute function, assert results ====
Example
Example
* ''"4simpleForm"''
* ''"4simpleForm"''
Line 3,017: Line 4,005:
#** Use this [[Reactjs_basics#App_example|ToDo list]] example as a skeleton
#** Use this [[Reactjs_basics#App_example|ToDo list]] example as a skeleton


=== Testing the Business Logic ===
=== Testing the Business Logic ===
[[File:JestJoke.jpeg|300px]]
[[File:JestJoke.jpeg|300px]]


==== Test Doubles ====
==== Test Doubles ====
* Used to verify interactions with collaborating objects
* Used to verify interactions with collaborating objects
* Spies and stubs
* Spies and stubs
* Extracting helper code into modules (readability - shorter and clearer)
* Extracting helper code into modules (readability - shorter and clearer)


==== Examples ====
==== Examples ====
* ''"6testDoubles"''
* ''"6testDoubles"''


==== Exercises ====
==== Exercises ====
# Extend ''6testDoubles''
# Extend ''6testDoubles''
#* Add a test to CustomerForm - the error state is cleared when the form is submitted again
#* Add a test to CustomerForm - the error state is cleared when the form is submitted again
Line 3,038: Line 4,026:
# In '''ftj''' app refactor 'adding new jewel' into another component
# In '''ftj''' app refactor 'adding new jewel' into another component


=== Testing the User Interface ===
=== Testing the User Interface ===
* Loader and render, shallow rendering
* Loader and render, shallow rendering
* Forms
* Forms
Line 3,046: Line 4,034:
* Testing GraphQL
* Testing GraphQL


==== Loader and render, shallow rendering ====
==== Loader and render, shallow rendering ====
* Stubbing out components
* Stubbing out components
** '''Loader''' components correctly instantiate '''rendering''' components.  
** '''Loader''' components correctly instantiate '''rendering''' components.  
Line 3,054: Line 4,042:
* Examples - ''"8forms"''
* Examples - ''"8forms"''


==== Forms ====
==== Forms ====
* Client-side validation
* Client-side validation
* Handling server errors
* Handling server errors
Line 3,069: Line 4,057:
#* Validate adding new jewel, not allow when it exists already
#* Validate adding new jewel, not allow when it exists already


==== Filtering and Searching Data ====
==== Filtering and Searching Data ====
* More complex user interactions (between UI and an API)
* More complex user interactions (between UI and an API)
* Order of adding features
* Order of adding features
Line 3,086: Line 4,074:
#* Allow to search for jewels
#* Allow to search for jewels


==== Testing Router ====
==== Testing Router ====
* General rules for testing React Router
* General rules for testing React Router
* Building a root component
* Building a root component
Line 3,096: Line 4,084:
#* Make router for showing list of jewels and a single jewel
#* Make router for showing list of jewels and a single jewel


==== Testing Redux ====
==== Testing Redux ====
* Testing a Redux saga
* Testing a Redux saga
* Asynchronous requests with saga
* Asynchronous requests with saga
Line 3,108: Line 4,096:
#* Add button "RemoveAll"
#* Add button "RemoveAll"


==== Testing GraphQL ====
==== Testing GraphQL ====
* Testing the Relay environment
* Testing the Relay environment
* Building the GraphQL reducer
* Building the GraphQL reducer
Line 3,118: Line 4,106:
<!-- # For '''ftj''' -->
<!-- # For '''ftj''' -->


=== Running Snapshot Tests ===
=== Running Snapshot Tests ===
[[File:JestSnapshot.png|600px]]
[[File:JestSnapshot.png|600px]]


Line 3,135: Line 4,123:
** Separate game description into new presentational component
** Separate game description into new presentational component


=== Troubleshooting ===
=== Troubleshooting ===
*'''jest-dom''' methods
*'''jest-dom''' methods
** allow to write more descriptive test code
** allow to write more descriptive test code
Line 3,146: Line 4,134:
** Examples - ''"rtlExamples"''
** Examples - ''"rtlExamples"''


==== RTL examples ====
==== RTL examples ====
* 1 - test-utils VS enzyme VS rtl
* 1 - test-utils VS enzyme VS rtl
* 2 - presentational, debug
* 2 - presentational, debug

Latest revision as of 01:03, 14 April 2025


Reactjs

Reactjs Training Materials

Introduction

  • Developed by engineers at Facebook
    • "A JavaScript library for building user interfaces"
  • React enables websites to display
    • Complex animations
    • Large volumes of data
    • Other memory-heavy tasks without slowing down

Intro Con't

  • Declarative
  • Component-Based
  • Reusable and independent

Declarative

  • Declarative views make the code more predictable and easier to debug
  • Very easy way to create interactive UIs
  • Simple views for each state in the application
  • Efficient update and render of the right components when the data changes

Component-Based

  • Encapsulated components that manage their own state
    • Compose them to make complex UIs
  • Component logic is written in JavaScript instead of templates
    • Easy to pass rich data through the app
    • In the same time keeps state out of the DOM

Reusable

  • Independent from technology stack used in the whole project
  • We can develop new features without rewriting existing code
  • Can render on the server (for example using Nodejs)
  • Can power mobile apps using React Native

Design principles behind React

  • JSX
  • Rendering Elements
  • Components and props

JSX

Rendering Elements

Building your first component

  • Component lets split the UI into independent, reusable and isolated pieces
  • Components are like JavaScript functions (conceptually)
    • Accept arbitrary inputs (called "props")
      • Props are read-only (function can't change it's own props)
    • Return React elements describing what should appear on the screen
  • The simple one-page app example
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <script crossorigin>
        // const process = { 'env': { 'NODE_ENV' : 'development' } };
    </script>
    <script crossorigin src="https://unpkg.com/react@18.3.1/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  </head>
  <body>
    <div id="rooty"></div>
    <script type="text/babel">

      ReactDOM.createRoot(
        document.getElementById('rooty')
      ).render(<h1>Hi Universe!</h1>);

    </script>
  </body>
</html>

How components work in React

  • Components implement a render() method
    • Takes input data and returns what to display
    • Often uses an XML-like syntax called JSX
    • Can access input data passed into the component via this.props
    • Input data passed into the component can be accessed by render() via this.props

Example 1

        //// JSX code
        class HelloMessage extends React.Component {
            render() {
            // JSX element treated as JavaScript expression:
            // can be saved in a variable, passed to a function, stored in an object or array, etc
                return <div>Hello {this.props.name}</div>;
            }
        }

        ReactDOM.createRoot(document.getElementById('rooty')).render(<HelloMessage name="Robert" />);


        //// COMPILED JS
        // class HelloMessage extends React.Component {
        //     render() {
        //         return React.createElement(
        //         "div",
        //         null,
        //         "Hello ",
        //         this.props.name
        //         );
        //     }
        // }

        // ReactDOM.createRoot(document.getElementById('rooty')).render(React.createElement(HelloMessage, { name: "Robert" }));

Exercise 1

Rewrite it into React function (-:

The component life cycle

  • In application with many components
    • Components are destroyed
    • Resources taken by the components have to be free up
  • Mounting
    • Whenever the component is rendered to the DOM for the first time
  • Unmounting
    • Whenever the DOM produced by the component is removed
  • Special(our own) methods called lifecycle hooks
  • setState() schedules updates to the component local state
  • "Common lifecycles"

Handling state in React

  • State is similar to props
    • But it is private and fully controlled by the component
    • Local state is a feature available only to classes
  • Component
    • Takes input data (accessed via this.props)
    • Can maintain internal state data (accessed via this.state)
    • When a component's state data changes, the rendered markup will be updated by re-invoking render()

Example 2

//// JSX part
class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {secondsElapsed: 0};
  }

  tick() {
    this.setState((prevState) => ({
      secondsElapsed: prevState.secondsElapsed + 1
    }));
  }
  // Lifecycle hooks
  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return (
      <div>Seconds Elapsed: {this.state.secondsElapsed}</div>
    );
  }
}

//// 'createRoot()' replaced 'ReactDOM.render()', below previous way (for React 17 and older)
// ReactDOM.render(<Timer />, document.getElementById('rooty'));
ReactDOM.createRoot(document.getElementById('rooty')).render( <Timer /> );

//// COMPILED JS, rendering part only
// ( ... )
//   render() {
//     return React.createElement(
//       "div",
//       null,
//       "Seconds Elapsed: ",
//       this.state.secondsElapsed
//     );
//   }
// ( ... )

Exercise 2

Guess what? (-!

  • Write function'ish version of it - of course!

Managing state

  • React on its own does not provide built-in support for state management
  • The React community uses libraries
    • Redux
    • MobX
    • xstate
  • React state vs. Redux state

Redux

  • Relies on synchronizing data through a centralized and immutable store of data
  • Updates to that data will trigger a re-render of the application
  • State is updated in an immutable fashion
    • Sending explicit action messages which must be handled by functions called reducers
  • Because of the explicit nature, it is often easier to reason about how an action will affect the state of our program

MobX

  • Relies on functional reactive patterns
    • State is wrapped through observables and passed through as props
    • Keeps state fully synchronized for observers
      • Simply marks state as observable.
  • Already written in TypeScript

xstate

React state vs. Redux state

  • Use React state for things that don’t matter globally
    • The form is one-off
    • The form’s data isn’t needed outside of it
    • The form’s state is so complex, managing all of that with Redux would be even worse
  • Use Redux for state that does matter globally
    • We want the input value to outlive the component
    • We have to have several instances of the form, synced
    • We want to share particular input between different forms
    • We want certain form state to have the impact on the rest of our app

Integrating React with other frameworks and plugins

Example 3, with ext plugin

// <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.js"></script>
// 
// JSX code
class MarkdownEditor extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {value: 'Type some *markdown* here! # babe - meow'};
  }

  handleChange(e) {
    this.setState({value: e.target.value});
  }

  getRawMarkup() {
    var md = new Remarkable();
    return { __html: md.render(this.state.value) };
  }

  render() {
    return (
      <div className="MarkdownEditor">
        <h3>Input</h3>
        <textarea
          onChange={this.handleChange}
          defaultValue={this.state.value} />
        <h3>Output</h3>
        <div
          className="content"
          dangerouslySetInnerHTML={this.getRawMarkup()}
        />
      </div>
    );
  }
}

ReactDOM.createRoot(document.getElementById('rooty')).render(<MarkdownEditor />);

Exercise 3

  1. Yup, you know what to do now, for sure (-'
    • You do, right? (-,
    • Function, obviously!
  2. Oh well, let's "kill one stone with many birds", shall we?
    • Create short training course materials: "How to kill one stone with many birds?"
    • Keep it really simple as a presentation - just a couple of slides (2 or 3)
    • Use this extension - https://github.com/rexxars/react-markdown

Bringing it all together - an application

  • Using props and state usually is enough to create the whole application
  • Below TODO list example
    • uses state to track the current list of items
    • Also tracks the text that the user has entered
  • Event handlers appear to be rendered inline
    • But they will be collected and implemented using event delegation

Example 4, app example

// JSX code
class TodoApp extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.state = {items: [], text: ''};
  }

  render() {
    return (
      <div>
        <h3>TODO</h3>
        <TodoList items={this.state.items} />
        <form onSubmit={this.handleSubmit}>
          <input onChange={this.handleChange} value={this.state.text} />
          <button>{'Add #' + (this.state.items.length + 1)}</button>
        </form>
      </div>
    );
  }

  handleChange(e) {
    this.setState({text: e.target.value});
  }

  handleSubmit(e) {
    e.preventDefault();
    var newItem = {
      text: this.state.text,
      id: Date.now()
    };
    this.setState((prevState) => ({
      items: prevState.items.concat(newItem),
      text: ''
    }));
  }
}

class TodoList extends React.Component {
  render() {
    return (
      <ul>
        {this.props.items.map(item => (
          <li key={item.id}>{item.text}</li>
        ))}
      </ul>
    );
  }
}

ReactDOM.createRoot(document.getElementById('rooty')).render(<TodoApp />);

Exercise 4

Yeah, again - I know (---;

  • Make it hooked!

Setting up your development environment

  • Create new app
  • Add React to an existing app

Create new app

Create new app Con't

  • Later we can adjust it and move to production
    • change the configuration
    • provide security (handling users via login and passwd, etc)
    • setup the db (mysql, mongodb, etc)
    • wire our existing web services, write new

Add React to an Existing App

  • No need to rewrite your app to start using React.
  • Start with small part of application (small widget), and see if it works well for our use case
  • React can be used without a build pipeline
  • It's recommended to set it up to be more productive
  • Build pipeline
    • Package manager (Yarn, npm, etc)
      • Vast ecosystem of third-party packages, easy to install or update them
    • Bundler (webpack, Browserify, vite, parcel, rsbuild, etc).
      • Lets to write modular code and bundle it together into small packages to optimize load time
    • Compiler (Babel, etc)
      • Lets to write modern JavaScript code that still works in older browsers

Exercise 5, Full envi, ts way

We will create simple application with 'vite' and 'nodejs'.
We will do it in steps, to cover most important elements of 'Reactjs'.
Each step will be explained separately.

Envi preparation steps (command line, in linux you might gonna need also 'sudo ' before the 'npm')
 npm create vite@latest mya -- --template react-ts
 cd mya
 npm i
 npm run dev

How to use vite with nodejs goodies

# enabling app in dev envi
npm run dev

# builds the app for production to the `build` folder
npm run build
  1. Step 1
    • Looking at the template code
  2. Step 2
    • change the file src/App.tsx
      import './App.css';
      
      function App() {
        return (
          <div className="App">
            <p>Hi Universe!</p>
          </div>
        );
      }
      
      export default App;
      
  3. Step 3 (tricky one!)
    • Create new component here components/Text.tsx
      const Text = () => {
          return (
              <p className="text">Lorem ipsum</p>
          ); 
      }
      
      export default Text;
      
  4. Step 4
    • Modify main component src/App.tsx
      import './App.css';
      import Text from './components/Text';
      
      function App() {
        return (
          <div className="App">
            <p>Hi Universe!</p>
            <Text />
          </div>
        );
      }
      
      export default App;
      
  5. Step 5
    • Understanding state. Change main component file again src/App.tsx
      import './App.css';
      import { React, useState } from 'react';
      import Text from './components/Text';
      
      function App() {
        const [text, setText] = useState('Not clicked!');
      
        function onButtonClick() {
          setText('Clicked!');
        }
      
        return (
          <div className="App">
            <p>Hi Universe!</p>
            <Text />
            <p>{text}</p>
            <button onClick={onButtonClick}>Click</button>
          </div>
        );
      }
      
      export default App;
      
  6. Step 6 (couple of fix-me cases here!)
    • Passing parameters(also state) to components.
      • Step 6.1. Modify file src/components/Text.tsx
        import PropTypes from 'prop-types';
        
        const Text = (props) => {
            return (
                <div>
                    <p className="text">{props.staticText}</p>
                    <p className="text">
                        {`Text from parent: ${props.clickText}`}
                    </p>
                </div>
            ); 
        }
        
        Text.propTypes = {
            staticText: PropTypes.string.isRequired,
            clickText: PropTypes.string.isRequired
        }
        
        export default Text;
        
      • Step 6.2. Modify main component file src/App.tsx
        import './App.css';
        import { React, useState } from 'react';
        import Text from './components/Text';
        
        function App() {
          const [text, setText] = useState('Not clicked!');
        
          function onButtonClick() {
            setText('Clicked!');
          }
        
          return (
            <div className="App">
              <p>Hi Universe!</p>
              {/* <Text /> */}
              <Text
                // Comment out the line below and check the 'console'
                staticText="Text from child component" 
                clickText={text} 
              />
              <p>{text}</p>
              <button onClick={onButtonClick}>Click</button>
            </div>
          );
        }
        
        export default App;
        

Defining our components' parent/child relationships

Lifting state up

  • A single "source of truth" for any data that changes
    • The state is first added to the component that needs it for rendering
    • Then, if other components also need it, we can lift it up to their closest common ancestor
    • Don't try to sync the state between different components, rely on the top-down data flow
  • More "boilerplate" code than in two-way binding approaches
    • But it takes less work to find and isolate bugs
      • any state "lives" in some component and that component alone can change it
      • the surface area for bugs is greatly reduced
    • We can also implement any custom logic to reject or transform user input
  • If something can be derived from either props or state, it probably shouldn’t be in the state

Containment

  • To reuse code between components
    • Composition model is recommended instead of inheritance
  • For not known children AOT in generic components
    • pass children elements directly with {props.children}
      • by nesting the JSX
    • For multiple generic "barrels" in a component use our own convention
      • pass our own React elements as props like any other data

Specialization

  • Components as special cases of other components
    • PublicCourseEvent and ConsultancyEvent are special cases of CourseEvent
  • Use composition
    • more "specific" component renders a more “generic” one
    • and configures it with props
  • To reuse non-UI functionality between components
    • extract it into a separate JavaScript module
    • the components may import it and use its constructs without extending it

Event handling and conditional rendering

Container vs Presentational Components

"Remember, components don’t have to emit DOM. They only need to provide composition boundaries between UI concerns"
  Dan Abramov (Co-author of Redux and Create React App)

Presentational Components

Dan Abramov's presentational components:

  • Are concerned with how things look
  • May contain both presentational and container components** inside, and usually have some DOM markup and styles of their own
  • Often allow containment via this.props.children
  • Have no dependencies on the rest of the app, such as Flux actions or stores
  • Don’t specify how the data is loaded or mutated
  • Receive data and callbacks exclusively via props
  • Rarely have their own state (when they do, it’s UI state rather than data)
  • Are written as functional components unless they need state, lifecycle hooks, or performance optimizations
  • Examples: Page, Sidebar, Story, UserInfo, List

Container Components

Dan's container components:

  • Are concerned with how things work
  • May contain both presentational and container components inside
    • but usually don’t have any DOM markup of their own except for some wrapping divs,
    • and never have any styles
  • Provide the data and behavior to presentational or other container components
  • Call Flux actions and provide these as callbacks to the presentational components
  • Are often stateful, as they tend to serve as data sources
  • Are usually generated using higher order components
    • such as connect() from React Redux, createContainer() from Relay, or Container.create() from Flux Utils,
    • rather than written by hand
  • Examples: UserPage, FollowersSidebar, StoryContainer, FollowedUserList

What are the benefits

  • Better separation of concerns
    • We understand our app and our UI better by writing components this way
  • Better reusability
    • We can use the same presentational component with completely different state sources
    • and turn those into separate container components that can be further reused
  • Presentational components are essentially our app’s palette
    • We can put them on a single page and let the designer tweak all their variations without touching the app’s logic
    • We can run screenshot regression tests on that page
  • This forces us to extract layout components such as Sidebar, Page, ContextMenu and use this.props.children
    • instead of duplicating the same markup and layout in several container components

Designing our React app

  • Prepare dry dummy interface
  • Break the UI into a Component Hierarchy
  • Build a static version in React (just props, no state yet)
  • Identify complete and minimal representation of UI state
  • Identify where our state should Live
  • Add inverse data Flow

Identify the minimal representation of UI state

Criteria for each piece of data (yes = probably not state)

  • Passed in from a parent via props?
  • Remain unchanged over time?
  • Computable based on any other state or props in our component?

Identify where our state should Live

For each piece of state in our application

  • Identify every component that renders something based on that state
  • Find a common owner component
    • a single component above all the components that need the state in the hierarchy
  • Either the common owner or another component higher up in the hierarchy should own the state
  • If we can’t find a component where it makes sense to own the state
    • create a new component simply for holding the state
    • and add it somewhere in the hierarchy above the common owner component

Main Exercises

  1. Pesel app
    • Migrate from jQuery to React
      • do it with React functions (-----:
      • Separate validations into JS/TS module
  2. Jewel app
    1. First migrate it from Angular to React - yes, functioooooooons ruuuleeeeezzzzzz (--;
      • follow the best practices - design first, then code it (check again this Approach)
    2. Improve it with form element
    3. Extend it with adding new jewel to model
      • Add custom validation - adding same jewel not allowed
    4. Make sure gamers can see the list of available jewels
    5. Wrap it into simple navigation - use react-router module
      • Home page - shows only description of the game
      • Play it - redirects to the game itself
      • Show jewels - view with all the gems
      • Add Jewel - new jewel form
      • Pesel - form from the previous exercise
      • Simple - our first full example (clicked or not clicked)
    6. Make a view with single jewel
      • Improve our jewel - add description field
    7. Allow to remove one jewel
      • Extend it - removal of all jewels
    8. Make one jewel editable

Handling Forms in React, classes way

Collapsed by default, old-school way here only

Using context instead of props

  • Passing props can become inconvenient if
    • we need to pass through many components
    • many components need the same info
  • Context makes some information available
    • to any component in the tree below the parent
    • no matter how deep it is
    • no need to explicitly passing through props
  • The context itself does not hold the information
    • it only represents the kind of information we can provide or read from components
  • Related docs:
  • Exercise
    • Try to re-design our Ftj form with context
      • What can you say?
      • What could be in the custom context there and what not (or shouldn't)?

Routing in React

  • Static Routing
    • configuration happens before our app renders
  • Dynamic Routing
    • routing that takes place as our app is rendering
  • Nested routes
  • Responsive routes
  • https://reactrouter.com/en/main
  • Router framework

npx create-react-router@latest my-react-router-app

Adding Router lib

npm install react-router-dom

Dynamic routing

  • Routers
  • Route Matching
  • Main Route Props
  • Navigation

Routers

  • A router component at the core
  • Creates a specialized history object
  • For web projects ('react-router-dom' lib)
    • <BrowserRouter> - a server that responds to requests
    • <HashRouter> - a static file server
  • For mobiles ('react-router-native' lib)
    • <NativeRouter>

Route Matching

  • Comparing a <Route>'s path prop to the current location’s pathname
  • When matches it will render its content, otherwise null
  • A <Route> with no path will always match
  • Route matching components
    • <Route>
      • include to render content based on the location
      • list a number of possible <Route>s next to each other
    • <Routes>
      • used to group <Route>s together

Main Route Props

  • element - should be used for an existing element/component
    • React.Component, functional component, just element
  • path - the url's part

Navigation

  • <Link> - component to create links
  • <NavLink> - a special type of <Link>
    • can style itself as "active" when its to prop matches the current location
  • <Navigate> - to force navigation

Router example

import { Routes, Route, Link } from "react-router-dom";

const Home = () => (
  <div>
    <h2>Home</h2>
    <p>Navigation example</p>
    {/* <YourHomeComponent /> */}
  </div>
);

const About = () => (
  <div>
    <h2>About</h2>
    <p>What are we doing here?</p>
  </div>
);

const MyApp = () => {
    return (
        <div>
          <nav>
            <li>
              <Link to={"/"}>Home</Link>
            </li>
            <li>
              <Link to={"/about"}>About</Link>
            </li>
          </nav>
          <hr />
          <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/about" element={<About />} />
          </Routes>
        </div>
    );
}

export default MyApp;

Nested Routes

  • Router has no 'nesting' API
  • Route is just a component (like div, etc)

Nested routes example

import { Routes, Route, Link, Outlet } from "react-router-dom";
import Timer from "./Timer";

function Home() {
    return ( <div>HOME page</div> );
}

function Messages() {
    return ( <div><p>Message 1</p><p>Message 2</p></div> );
}

function Todos() {
    return (
        <>
            <div>
                <h4>Todo 1</h4>
                <p>Fix the car</p>
            </div>
            <div>
                <h4>Todo 2</h4>
                <p>Spend some time with wife</p>
            </div>        
        </>
    );
}

function Menu() {
    return (
      <div>
        <h2>Menu</h2>
        <nav>
            <li><Link to={"messages"}>Messages</Link></li>
            <li><Link to={"todos"}>Todos</Link></li>
        </nav>
        {/* This element will render either <Messages> when the URL is
            "/messages" or <Todos> at "/todos"
        */}
        <Outlet />
      </div>
    );
}
  
function Nested() {
    return (
        <div>
            <h1>Main Menu</h1>            
            <nav>
                <li><Link to={"/"}>Home</Link></li>
                <li><Link to={"/sub"}>Sub</Link></li>
            </nav>            
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="sub" element={<Menu />}>
                    <Route path="messages" element={<Messages />} />
                    <Route path="todos" element={<Todos />} />
                </Route>
            </Routes>
        </div>        
    );
}

export default Nested;
Router Exercises
  1. Replace todos component with our Todos from this Example
  2. Add another link on the parent level
  3. Create 2nd level sub-menu
    • It should start from Messages
    • Put one link there only - showing some Contact details


Managing state with Redux

  • State of our app is stored in an object tree inside a single store
  • To change the state tree we emit an action, an object describing what happened
  • To specify how the actions transform the state tree, we write reducers
  • Doesn't support many stores
    • instead of adding stores, we split the root reducer into smaller reducers
    • independently operating on the different parts of the state tree
  • https://redux.js.org/introduction

Examples and exercises

  1. reduxExamples_
    • crud, jwt, hooks
  2. redux-toolkit-crud - fix me, again (-;
  3. Rewrite our dynamic form example with Redux
  4. Rewrite our FTJ app with Redux

Managing state with Mobx

  • Mobx is not a container for the state (not like Redux)
  • To make objects trackable for MobX
    • Use the @observable decorator or observable(object or array) functions
  • To create functions that can automatically derive their value from the state
    • Use the @computed decorator
  • To automatically run functions that depend on some observable state
    • Use autorun
    • useful for logging, making network requests, etc
  • To make React components truly reactive
    • Use the @observer decorator from the mobx-react package
    • They will update automatically and efficiently (also in large complex applications with large amounts of data)
  • https://mobx.js.org/

Example

// Define your state and make it observable
import {observable} from 'mobx';

var appState = observable({
    timer: 0
});


// Create a view that responds to changes in the State
import {observer} from 'mobx-react';

@observer
class TimerView extends React.Component {
    render() {
        return (<button onClick={this.onReset.bind(this)}>
                Seconds passed: {this.props.appState.timer}
            </button>);
    }

    onReset () {
        this.props.appState.resetTimer();
    }
};

ReactDOM.render(<TimerView appState={appState} />, document.body);


// Modify the State
appState.resetTimer = action(function reset() {
    appState.timer = 0;
});

setInterval(action(function tick() {
    appState.timer += 1;
}), 1000);

Mobx Exercise

Hooks

  • Let use: props, state, context, refs, lifecycle
    • without writing a class
    • provide new way to combine them
  • Optional and backwards-compatible
    • Work side-by-side with existing code - can be adopted gradually
  • Functions that:
    • let "hook into" React state and lifecycle features from function components
    • don’t work inside classes

Hooks Con't

  • Allow to reuse stateful logic without changing component hierarchy
  • Let split one component into smaller functions based on what pieces are related
    • Examples: setting up a subscription or fetching data
  • Common: useState, useEffect, useContext, useRef
  • Custom hooks - reusing stateful behavior between different components
  • Rules about calls
    • only at the top level (not in: loops, conditions, nested f)
    • only from function components (not from regular js) or custom hooks
  • Examples

Hooks Examples

  • useState()
  • useMemo()
  • useId()
  • useCallback()
  • useEffect()
  • useRef()
  • useContext()

useState()

  • lets to add a state variable to our component which returns an array with two values
    • Current State
    • Set Function
  • we can pass on initial value as well like below:- 234 and 'Gulliver'
import { useState } from 'react';

function Invoice() {
  const [invNo, setInvNo] = useState(234);
  const [companyName, setCompanyName] = useState('Gulliver');
}

useMemo()

  • lets to cache the result of a calculation between re-renders
    • prevents the unnecessary renders in our React Application
import { useMemo } from 'react';

function InvoicesList({ invoices, tab }) {
  const visibleInvoices = useMemo(
   () => filterInvoices(invoices, tab),
   [invoices, tab]
  );
}

useId()

  • for generating unique IDs that can be passed to accessibility attributes
    • which let to specify that two tags are related to each other
    • where we can use useId() generated ids instead of hardcoding them
import { useId } from 'react';

function PasswordField() {
  const passwordClueId = useId();

  return (
   <>
    <input type="password" aria-describedby={passwordClueId} />
    <p id={passwordClueId}>
   </>
  )
}

useCallback()

  • lets to cache a function definition between re-renders
  • useCallback caches a 'function' and useMemo caches a 'value/result of a calculation'
import { useCallback } fom 'react';

export default function BookingPage({ bookingId, referrer, theme }) {
  const handleSubmit = useCallback((leadDetails) => {
   post('/booking/' + bookingId+ '/buy' {
    referrer,
    leadDetails,
    });
   }, [bookingId, referrer]);
(...)

useEffect()

  • lets to perform side-effects in the component
  • side effects basically is an action which connects the component to the outside world
(...)
useEffect(() => {
  const connection = createConnection(serverCourseEventUrl, courseEventRoomId);
  connection.connect();
  return () => {
   connection.disconnect();
  };
 }, [serverCourseEventUrl, courseEventRoomId]);
(...)

useRef()

  • lets to reference a value that's not needed for rendering
  • similar to useState, the difference is it doesn't cause a re-render when the value changes
import { useRef } from 'react';

function RefreshDadesktopSandbox() {
  const intervalRef = useRef(0);
  const inputRef = useRef(null);
(...)
}

useContext()

  • lets to read and subscribe to context from our component
  • just like a data store (Redux, Mobx, etc)
import { useContext } from 'react';
import { ThemeLeadContext} from './ThemeLeadContext.js';

function SkinForLead() {
  const theme = useContext(ThemeLeadContext);
(...)
  return (
    <section className="np-lead-section">
      <ThemeLeadContext value={leadType + 1}>
        {children}
      </ThemeLeadContext>
    </section>
  );
}

Custom Hook

We can define our own hooks to use state and other React features without writing a class

  • As a function, it takes input and returns output
  • name starts with use (useQuery, useMedia.., etc)
  • returns a normal, non-jsx data (not like in functional component)
  • can use other hooks such as useState, useReF.. and other custom hooks (-;
  • some libraries also provide hooks - useForm (React Hook Form), useMediaQuery (MUI), useQuery (React Query), etc

Custom Hook Con't

Benefits:

  • Completely separate logic from user interface
  • Reusable in many different components with the same processing logic
    • Therefore, the logic only needs to be fixed in one place if it changes
  • Share logic between components
  • Hide code with complex logic in a component, make the component easier to read

When to use:

  • piece of code (logic) is reused in many places
  • logic is too long and complicated
  • we write custom react library (-:
  • ... ? (your ideas, please!)
  • Example - let's look at useNavigate() in react-router lib
  • Example/Exercise - react-ts-api


React Query

  • Most state management libraries (incl Redux)
    • good for working with client state
    • not for server state
      • persists remotely in a location the client side cannot control
      • can become outdated - we need to make asynchronous APIs for fetching and updating
  • React Query - one of the best libraries for managing server state
    • It helps to fetch, cache, synchronize and update data without touching any global state

React Query Con't

Helps:

  • to remove complicated and misunderstood code and replace with several React Query logic
  • easier to maintain and build new features without worrying about wiring up new server state data sources
  • make our application feel faster and more responsive
  • save bandwidth and increase memory performance
  • Example/Exercise - reactq-axios-ts


React Hook Form

  • Less code, more performant
    • reduces the amount of code we need to write
    • removes unnecessary re-renders
  • Isolate re-renders
    • ability to isolate component re-renders -> better performance on our page or app
  • Subscriptions - better performance
    • ability to subscribe to individual input and form State update without re-rendering the entire form
  • Faster mounting (than Formik, ReduxForm, etc)
  • More here https://react-hook-form.com/
  • Example/Exercise - react-hook-form-ts


MUI

MUI System

  • Set of utilities (Flexbox, Shadows, Typography, etc)
    • for quickly applying styles to components from: BUI, MUI, JUI
    • or to any third-party components
  • Helps to build custom designs more efficiently
    • and to rapidly lay out custom designs
  • Set of flexible, generic wrapper components (Box, Container, Grid, Stack)
    • can be quickly customized using the sx prop
      • styles can be defined directly within the components themselves
      • simplifies bulky and redundant definitions of styled-components
      • ensures consistency in one-off styles too
  • CSS tricks - https://css-tricks.com/snippets/css/a-guide-to-flexbox/

BaseUI

  • Library of "unstyled" React components (standalone) and low-level hooks
  • Set of foundational "headless" components
    • can be built with using any styling solution
    • no need to override any default style engine or theme
  • "skeletal" version of Material UI

MaterialUI

MaterialUI Exercise

  1. Here choose the environment
  2. Next decide about the template (or not)
  3. Finally try to use this builder

JoyUI

React with Typescript

React Native

  • Creates native apps for Android, iOS
  • Written in JavaScript — rendered with native code
  • Seamless Cross-Platform
    • React components wrap existing native code
    • interact with native APIs via React’s declarative UI paradigm and JavaScript
  • Setup and Using
    • Expo Go - quick and easy, but restricted SDK libs
    • Native CLI - longer to prepare, but gives access to many external libs
  • Related docs, examples

React Native Con't

npx create-expo-app nativeExample
cd nativeExample
npx expo start

Later on

  • connect on the same Wifi with phone
  • install Expo Go app on phone
  • scan the QR

Full stack with React

npx create-next-app@latest ftjrn
cd ftjrn
npm run dev

nextjs with xstate

npx create-next-app@latest --example with-xstate myax

nextjs with hook-form

npx create-next-app --example with-react-hook-form myahf

nextjs with forms

npx create-next-app --example next-forms myaf

nextjs with mongodb and tailwind

SIMPLE - with tailwind css

npx create-next-app --example with-mongodb myam

STRONGER - with crud actions

npx create-next-app --example with-mongodb-mongoose myams

Server side rendering and SEO

  • Next.js
    • Server-rendered by default
    • Automatic code splitting for faster page loads
    • Simple client-side routing (page based)
    • Webpack-based dev environment which supports Hot Module Replacement (HMR)
    • Able to implement with Express or any other Node.js HTTP server
    • Customizable with our own Babel and Webpack configurations
    • Out-of-the-box tools for setting HTML tags for SEO
    • https://nextjs.org/learn/
    • https://zeit.co/blog/next
  • react-seo npm package
  • react-examples
    • 14-Server-Rendering
    • quickfix: remove internal from package.json npm run ssr-... scripts

Why not angular?

Debugging

Upgrading, Refactoring

React's Lego

Testing your React web application

JestGeneric.jpg

Jest Intro

  • No config
    • Supports - Babel, TypeScript, Node, React, Angular, Vue, etc
  • "Snapshotting"
  • Isolated
  • Good API

Overview of Jest

  • Zippy, secure
    • tests have unique global state
    • reliably runs tests in parallel
    • runs previously failed tests first
    • re-organizes runs based on how long test files take

Overview Con't

  • Code coverage (--coverage)
    • No setup needed, collects from entire projects (incl untested files)
  • Simple mocking
    • custom resolver for imports, mocks any object outside of scope
    • rich Mock Functions API with readable syntax

Overview

  • Whacking exceptions
    • rich context for test-fail: toStrictEqual, toHaveProperty, toMatchSnapshoot, toThrowError, etc
  • Well-documented
    • maintained by Christopher Pojer from Facebook and contributors within the community
  • Requires little configuration
  • Extendable

Benefits of testing

Why and how

  • Interfaces depend on browsers, user interactions, and many other variables
    • implementing an effective testing strategy is difficult
  • Consistent results are often affected by false negatives due to different factors (i.e. network)
  • Frequent updates in UI: improve the experience, maximize conversions, new features
  • Tests are hard to write and maintain ==> developers are less prone to cover their applications
  • Tests make developers more confident ==> speed and quality
  • Well tested code and well written tests ==> it works and is ready to ship
    • Easier to refactor the code ==> functionalities do not change during the rewrite
  • Running tests using Node.js and the console ==> faster, more predictable

Benefits of testing Con't

  • Is new feature affecting other parts of the application
    • Tests avoid regressions ==> tell if the new code breaks the old tests
  • Greater confidence in writing new features ==> faster releases
  • Code base more solid ==> new bugs can be reproduced, fixed, and covered by tests
  • Testing components is easy - well organised and assigned responsibilities and boundaries
    • Well written components (pure, composable, reusable) ==> can be tested as simple functions
  • Main goals to test
    • given different sets of props, components output is always correct
    • covering all the various states that a component can have
      • clicking a button ==> tests to check all the related event handlers

Benefits of testing Con't - tdd, design

  • Tests can verify the component's behavior on edge cases
    • all the props are null, or there is an error
  • With React we can mount a tree of components and test the integration between them
  • Techniques - test-driven development (TDD)
    • Applying TDD ==> writing the tests first and then writing the code to pass the tests
    • Force to think more about the design before implementing ==> usually leads to higher quality

Setting up the Testing Environment

  • Examples
    • Folder "testing"
    • Tests from "react-training"
  • Enzyme - helps to test Components' output
    • manipulate, traverse, and in some ways simulate (like with jQuery)
  • Cypress - e2e framework

Installing and Configuring Jest

  • Initial setup
## Installing
npm install --save-dev jest

## Config
jest --init

## The rest depends on other tools: babel, webpack, typescript, etc
  • More here:
    • jestjs.io/docs/getting-started#additional-configuration

Running Watch Mode to Test File Changes

live-server --cors #manual tests with real browser
jest -o  #runs only with uncommited changes (git, mercurial)
jest --watch  #runs jest -o by default
jest --watchAll  #runs all tests

### Interactively - Watch Usage: Press w to show more.

## Watch Usage
# › Press f to run only failed tests.
# › Press o to only run tests related to changed files.
# › Press q to quit watch mode.
# › Press p to filter by a filename regex pattern.
# › Press t to filter by a test name regex pattern.
# › Press Enter to trigger a test run.

More here: jestjs.io/docs/cli

Running Browser Tests through Node

npm test  #via 'create-react-app' it uses by default the extension called 'react-scripts'

Testing a Sample JavaScript Application

TDD - Test Driven Development

  • Red-Green-Refactor cycle
  • Triangulation
    • The more specific tests get, the more general production code needs to get
    • Always implement the simplest thing that will possibly work
    • Hard-coding when it's possible and to get to the real implementation - add more tests
  • AAA rule: Arrange(Assemble), Act, Assert
  • Design principles
    • DRY - Don't Repeat Yourself
      • Drying up tests when refactoring
    • YAGNI - You Ain't Gonna Need It
      • Hold off adding anything to project until we're really sure that it's necessary (adding a user story, a customer asks for it, etc)
      • For example create-react-app application template - favicon.ico, a sample logo, CSS files, etc

TDD Con't

TddJestReact.png

Good vs Great

Good test Great test
Arrange: Sets up test dependencies Short
Act: Executes production code under test Descriptive
Assert: Checks expectations are met Independent of other tests
lol Has no side-effects

Examples

  • Example 1 - "1oneDetailOnly"
  • Example 2 - "2viewsListAndDetail"

Design upfront

JewelsListViewDesign.png

Exercises

  1. ("ex1") Display in the UI the rest of fields from model
    • Wrap data into HTML table
    • Add a heading to Appointment to make it clear which appointment time is being viewed
    • Prepare fake data generator (DRY)
  2. Prepare new Find The Jewel project (name it ftj)
    • Use create-react-app ( cd ; cd Documents/reactjsMaterials ; npx create-react-app ftj )
    • Make oneJewel component and test it
    • Make listJewels component and test it
      • Clicked jewel button should be different then the rest of jewel buttons (add css class dynamically)

Testing a React App

  • Simple form and user input
  • Testing React Components
  • About Stateful Components

Set up, execute function, assert results

Example

  • "4simpleForm"

Exercises

  1. Add dropdown - choosing stylist (before time slot), filtering based on required service
    • reflect stylists availability and the choice (update the table)
  2. Make form for adding new jewel and test it
    • Refactor the app into JewelGame class component with state

Testing the Business Logic

JestJoke.jpeg

Test Doubles

  • Used to verify interactions with collaborating objects
  • Spies and stubs
  • Extracting helper code into modules (readability - shorter and clearer)

Examples

  • "6testDoubles"

Exercises

  1. Extend 6testDoubles
    • Add a test to CustomerForm - the error state is cleared when the form is submitted again
    • Update the AppointmentForm tests to use jest.fn(), jest.spyOn(), and all of the helpers
    • Extend AppointmentForm so that it submits an appointment using a POST request to /appointments (endpoint returns a 201 Created status without any body)
    • Update the tests in AppointmentsDayView to use the helpers
  2. Add engine component in ftj app
    • Test it with doubles
  3. In ftj app refactor 'adding new jewel' into another component

Testing the User Interface

  • Loader and render, shallow rendering
  • Forms
  • Filtering and Searching Data
  • Testing Router
  • Testing Redux
  • Testing GraphQL

Loader and render, shallow rendering

  • Stubbing out components
    • Loader components correctly instantiate rendering components.
  • Shallow rendering
  • Stubbing and spying on child components - avoids running their side effects (fetch requests, etc)
    • Easily assert that we set their props correctly
  • Examples - "8forms"

Forms

  • Client-side validation
  • Handling server errors
  • Indicating submit
  • Examples - "8forms"
  • Exercises
  1. Within "8forms"
    • Clear any validation errors when corrected
    • Use the 'onChange' handler instead of 'onBlur' (faster interaction with user)
    • Disables the Submit button after submitting
    • Write tests all functions within the formValidation module
    • Simplify 'handleSubmit' - extract doSave func (pulls out the main body of the if statement)
  2. With ftj
    • Validate adding new jewel, not allow when it exists already

Filtering and Searching Data

  • More complex user interactions (between UI and an API)
  • Order of adding features
  • Tests help admit when we're doing the wrong thing
  • Examples - "10searchingPaging"
  • Exercises
  1. With "10searchingPaging"
    • Disable the 'Previous' button if the user is on the first page
    • Disable the 'Next' button if the current listing has fewer than 10 records on display
    • The /customers endpoint supports a limit parameter - specifies the number of records that are returned
      • Provide a mechanism for the user to change the number of records returned on each page
  2. For ftj
    • Allow to search for jewels

Testing Router

  • General rules for testing React Router
  • Building a root component
  • Using the location query string to store component state
  • Examples - "12router"
  • Exercises
  1. For ftj
    • Make router for showing list of jewels and a single jewel

Testing Redux

  • Testing a Redux saga
  • Asynchronous requests with saga
  • Switching out component state for Redux state
  • Shifting workflow to Redux
  • Examples - "13redux"
  • Exercises
  1. For todo_ts
    • Add button "RemoveAll"

Testing GraphQL

  • Testing the Relay environment
  • Building the GraphQL reducer
  • Examples - "14graphql"
  • Exercises
  1. With "14graphql"
    • Reshape the remaining fetch calls to use their GraphQL counterparts

Running Snapshot Tests

JestSnapshot.png

Snapshot Tests

  • Useful especially when testing presentational components
    • They do not manage state
    • Typically used to display data passed down from parent components as props
    • or to display hardcoded data directly in the component itself
  • Provided by Jest
    • We simply want to make sure the HTML output of a component does not change unexpectedly
  • Case scenario - developer does change the component's HTML structure
    • He adds another paragraph element with static text
    • Snapshot test will fail and provide a visual of the changes
  • Example - "rtlExamples", 2
  • Exercise - in ftj make a snapshot test
    • Separate game description into new presentational component

Troubleshooting

  • jest-dom methods
    • allow to write more descriptive test code
    • provide better context-specific error messages
  • Using RTL (React Testing Library)
    • Approach
      • render React elements into the Document Object Model (DOM)
      • select resulting DOM elements
      • make assertions on the expected resulting behavior
    • Examples - "rtlExamples"

RTL examples

  • 1 - test-utils VS enzyme VS rtl
  • 2 - presentational, debug
  • 3 - user events, interacting with APIs, again TDD
  • 4 - integration testing, 3rd party plugins
  • 5 - updating dependencies, enzyme2rtl, test-utils2rtl, best practices
  • 6 - tools and plugins, more best practices
  • 7 - e2e, cypress, cucumber

Photos Sources

  • kurierhistoryczny.pl/uploads/articles/237/stanczykmaly_rf89RS.jpg
  • cdn.shopify.com/s/files/1/0882/5118/products/Batman-The-Killing-Joke-Deluxe-Graphic-Novel-1008696_1024x1024.jpeg?v=1461690370
  • wpblog.semaphoreci.com/wp-content/uploads/2019/01/Snapshot_Testing_React_Components_with_Jest_-_Semaphore_CI-1024x575.png