React

From Sinfronteras
Jump to: navigation, search

https://react.dev/



React (also known as React.js or ReactJS) is a JavaScript library (not a Framework) for building user interfaces. It is maintained by Facebook.

React can be used as a base in the development of single-page or mobile applications.

React allows us to build component-based user interfaces (UI). A component is a self-contained standalone piece of functionality. For example, a drop-down menu or a collapsible panel can be implemented as a React component.


To understand what is React, we can said that, in general, React generates html using JS or TypeScript. You can choose if you want to do generate the html in the client site or in the server site. This course is focused about how to do it in the client site. In the sever site is more complicate and it is done just in some particular case.


We'll be follwing the official documentation of React:

https://reactjs.org/docs/getting-started.html#learn-react

It is recommended to start by the guide to main concepts.




Ejemplos comparacion con JS

import React from 'react';

import {
    IonButtons,
    IonCard,
    IonCardContent,
    IonCardHeader,
    IonCardSubtitle,
    IonCardTitle,
    IonContent,
    IonHeader,
    IonIcon,
    IonItem,
    IonLabel,
    IonList,
    IonListHeader,
    IonMenuButton,
    IonPage,
    IonTitle,
    IonToolbar
    } from '@ionic/react';


interface Home2Props{

}

interface Home2State{
    result: string;
    num1: string;
    num2: string
}

export default class Home2 extends React.Component<Home2Props, Home2State> {
    public constructor(props: Home2Props){
        super(props)
        this.state = {
            result: "",
            num1: "",
            num2: ""
        };
    }
    public render(){
        return (
            <IonPage>
                <div>
                    Hola
                    <input
                        type="string"
                        value={this.state.num1}
                        onChange={e => this._onTodoChangeInput1(e.target.value)}
                    />
                    <input
                        type="string"
                        value={this.state.num2}
                        onChange={e => this._onTodoChangeInput2(e.target.value)}
                    />   
                    <button onClick={() => this._add()}>Add</button>
                    <div> {this.state.result} </div>
                </div>
            </IonPage>
        );
    }
    private _add(){
        // The "+" before the variable conver is to convert from string to number
        const resultado = +this.state.num1  +  +this.state.num2;
        this.setState({result: resultado.toString()})
    }
    private _onTodoChangeInput1(value: string){
        this.setState({
             num1: value
        });
    }
    private _onTodoChangeInput2(value: string){
        this.setState({
             num2: value
        });
    }
}

async function getData(){
    const response = await fetch("http://www.apilayer.net/api/live?access_key=cb51ac5d62bd1e6713b4b6c6e015d6b6");
    const json = await response.json();
    // return json;
    return json.quotes.USDUUR;
}



Deploy a Node-Express and React Application

We have used PM2 as an application manager in order to keep our app running after we log out of the server:


Install PM2:

sudo npm install pm2 -g


Start the app:

pm2 start app.js
pm2 start app.js --name "my-api"  # Si queremos darle un nombre al proceso pm2


The display the list of applications currently managed by PM2:

pm2 list


To get more details about an app currently manged by PM2:

pm2 show <id|name>  # See the id display by the «pm2 list» command


Stop an application with this command (specify the PM2 App name or id):

pm2 stop <id|name>  # No elimina la app de la lista de procesos pmw, only stops it


Restart an application with this command (specify the PM2 App name or id):

pm2 restart <id|name>

sudo npm run build Now you want to delete the app from the PM2 process list. You just have to enter the following commands:

pm2 delete <id|name>



Node

To deploy the node application solo tuve que ejecutar:

pm2 start build/index.js --name "twitterclone-backend"

Ahora, no me acuerdo si tuve primero que buid la JS application (el directorio build) siguiendo algunos de los pasos en este post: https://medium.com/javascript-in-plain-english/typescript-with-node-and-express-js-why-when-and-how-eb6bc73edd5d

Lo que si he constatado la última vez que estuve revisando es que la siguiente parte de dicho post:

«To run the server in production mode:

npm run prod»

.. no funciona y no fue así que completé el deployment en production. En el «package.json» se encuentran los script propuestos por este post pare ejecutar el «npm rn prod»; pero me imagino que lo hice solo para probar pero no está funcionando.



React

https://facebook.github.io/create-react-app/docs/production-build

https://facebook.github.io/create-react-app/docs/deployment


https://medium.com/@seanconrad_25426/setting-up-a-create-react-app-with-pm2-and-nginx-on-digitalocean-1fd9c060ac1f


To deploy the react app I did:

sudo npm run build
pm2 start node_modules/react-scripts/scripts/start.js --name "twitterclone-frontend"



JSFiddle

https://jsfiddle.net/

JSFiddle is an online community for testing and showcasing user-created and collaborational HTML, CSS and JavaScript code snippets, known as 'fiddles'. It allows for simulated AJAX calls.



Execute some JS commands from the Web Browser console

You can use JS to access and modify the html and the css. You can, for example, create a new html element in the DOM.

Let's see how that works. From the Web Browser console, you can display the structure of the page and make changes on it. For example, you can display the body of the web page by executing:

document.body


You can make changes in the background color with:

document.body.bgcolor = "red"



Single Web Application

We said that we are creating a single web page App because what the server send to the client is only one page, which is that index.html page. Then, the Reanc Application can have as many pages as we want, but they are all generated in the client site.



The main characteristics of React

  • SimplicityReact: can seem very different from other libraries at first glance but its API is minimaland its programming model is actually quite simple. As a result, learning React or investigating the causes of an issue can be relatively simple tasks.
  • Component-based: We are going to learn more component-based UI development in the following section.
  • JavaScript APIs over custom APIs: The React API tries to use standard JavaScript APIs instead of custom APIs. For example, in React we can implement a loop while rendering elements using theJavaScript map function, instead of a custom API such as the ng-repeat API in Angular.
  • Highly influenced by functional programming principles: React is highly influenced by functional programming. The implications of these influences are very noticeable in many of the React API and design principles. The following are some of the main characteristics of React that can be considered as a direct result of its Functional Programming roots:
  • Immutability: React encourages us to use immutable objects and avoid state mutations.
  • Stateless: In React we have to worry a lot about the application's state. React encourages us to try to avoid it and to push it to the boundaries of our system.
  • Declarative over imperative: React encourages a declarative programming style over an imperative programming style.



Component-based UI development

React allows us to implement (SPAs) using a UI programming model known as component-based UI development. The main building block in a component-based UI development library or framework is a web component. A web component is a piece of the UI that is isolated, and self-contained. A web component encapsulates the HTML, CSS, and JavaScript.

When we build a component-based UI, we start by implementing the most basic web components such as inputs and buttons. We can call these components primitives. We then combine primitive components to build more sophisticated components and eventually we compose multiple advanced components to create the pages in our web application. However, the pages themselves can also be implemented as web components.



Virtual DOM

React uses a pattern known as virtual DOM (VDOM) (DOM: Document Object Model) where a virtual representation of a UI is kept in memory and synced with the real DOM by a library such as ReactDOM. The synchronization process is called reconciliation.

The virtual DOM allows us to update the DOM in a declarative way, so we don’t need to worry about how React updates the DOM internally. React can figure out how to efficiently update the DOM from the previous HTML tree to the next version in a very efficient way. You may have heard about React Fiber is the new reconciliation engine in React 16. Its maingoal is to enable incremental rendering of the virtual DOM.

It is important not to confuse the virtual DOM with the shadow DOM. The Shadow DOM is a browser technology designed primarily for scoping variables and CSS in web components. The virtual DOM is a concept implemented by libraries in JavaScript on top of browser APIs.



The main benefits of React

  • Simplier applications: The React programming model can simplify the development of complex web applications. In particular, when compared against other technologies that don't use a component-based web development model.
  • Fast user interfaces: The React rendering engine is very fasts and efficient. Using React can helpus to achieve an outstanding web application performance.
  • Re-usability: Web components are highly reusable by nature. They provide an abstraction for apiece of UI functionality so it can be re-used.
  • Test ability: React is highly influenced by some functional programming ideas. As a result, React encourages the implementation of stateless components and immutable properties. We will learn more about these principles later, but for now, we can say that these principles help our components to be unit tested with ease.
  • Powerful abstractions: JSX and the Virtual DOM a allow us to write declarative code and abstract us away from the DOM rendering and reconciliation details.
  • Ecosystem and community: The React community and there is a huge number of libraries and frameworks with a high degree of diversity.
  • Consistent user experience: Web components can help to create a consistent user experience across an entire application or even an organization as a whole. Web components can help our applications to adhere to style guides with ease.



The React ecosystem

As we have already learned, React is a library, not a framework.

React is inspired by the UNIX philosophy:

Do one thing and do it well.

So React is very good at one thing: allow developers to build UI components. However, React doesn't tinclude support for some of the most common features in SPAs. For example, React doesn't include any routing or navigation-related features, data fetching features or state management features among many others.

If we want to build a real-world professional SPA, it is highly likely that we will need some additionallibraries. The good news is that the React ecosystem is very extensive and we will almost certainly findan already existing library that can do what we are looking for.

The React ecosystem also follows the UNIX philosophy. This means that we will have to install a numberof tools because each tool will only do one thing. For example, the react-dom module renders the React application using the DOM API while the react-router-dom module can be used to implement routing in web browsers. These modules do only one thing, but they do it very well.

This architectural style has some pros and cons. We can say that React allows us to select the best tool for the job and does't decide anything for us. React gives us more options and, as a result, getting started with React can be being more time-consuming.

Also, because the Raact ecosystem is very diverse and sometimes each project comes from a different author, there is a higher risk of projects being abandoned.



Install React

We need to install the create-react-app command using npm. Because we are going to invoke the create-react-app command from our terminal, we need to install it as a global dependency using the -g flag:

npm install -g create-react-app



Creating a React project

In the past, creating a React application used to be a very tedious and complicated task. However,things have gotten much better over time and today we can auto-generate an entire React application in just a few minutes using the create-react-app command.

We are going to use the create-react-app to scaffold out a basic React app from the terminal in just a couple of minutes.

To generate a new React application with TypeScript support, we need to execute the following command:

create-react-app myreactapp --typescript

The preceding command will generate a folder named myreactapp the current directory. This operation can take a few minutes, so please be patient.

It is recommended to create the application in a directory that is unlikely to be affected by permissions-related restrictions. For example, you can use the CODE directory under your user's home directory (~/CODE/home).

If everything works correctly, a new folder named myreactapp should be created in the current directory and it should contain the following files and folders:

.
|--README.md
|--node_modules
|   `-- ...
|--public
|   |--favicon.ico
|   |--index.html
|   `--manifest.json
|--src
|   |--App.css
|   |--App.test.tsx
|   |--App.tsx
|   |--index.css
|   |--index.tsx
|   |--logo.svg
|   |--react-app-env.d.ts
|   `--serviceWorker.ts
|--package-lock.json
|--package.json
`--tsconfig.json

It is essential to ensure that the extension of some of the files such as the index.tsx is .tsx andnot .js. When we execute the create-react-app with the --typescript flag, we are creating a TypeScript project, and the file extension will be .tsx. However, if we forget the flag, the create-react-app command will generate a JavaScript project and the extension of the index.tsx file will be .js instead of .tsx. If the file extension is wrong, you should delete the entire project folder and create a new project using the --typescript flag.



package json

The project generated by the create-react-app command contains the following package.json file:

{
  "name": "myreactapp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@types/jest": "24.0.6",
    "@types/node": "11.9.4",
    "@types/react": "16.8.4",
    "@types/react-dom": "16.8.2",
    "react": "^16.8.2",
    "react-dom": "^16.8.2",
    "react-scripts": "2.1.5",
    "typescript": "3.3.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

As we can see in the preceding code snippet, the auto-generated package.json file contains some default npm scripts commands:

  • npm run start can be used to start running our React application using a local development server.
  • npm run build can be used to build our React application into an application bundle that we can deploy to a hosting provider.
  • npm run test can be used to execute the unit and integration tests in our React application.
  • npm run eject can be used to make some internal configuration public. One example is the Webpack configuration file. Webpack is a tool that is used to compile, minimize and bundle front-end applications into highly optimized files. We do this because it helps to reduce the loading times of our applications. However, configuring Webpack is a complex and tedious task. The create-react-app project hides this complexity from us. We are free to expose the complexity using the eject npm script.


Please note that it is not possible to rollback the changes performed by the eject npm script command.



tsconfig json

tsconfig json is another important file generated by the create-react-app command. This file contains the TypeScript compiler options.



Run the App

Now that we know about the npm scripts generated by create-react-app in the package.json file, we are going to run our application using the start command. When in the linux terminal you execute npm run start, this execute react-scripts start and start the App in the Web Browser:

npm run start


At this point, we should be able to see our React application displayed on the screen:

The default application generated by the create-react-app command

As we can see in the default application, the following message is displayed on the screen:

Edit src/App.tsx and save to reload

The local web server started by the npm run start command automatically reloads the React application when a change in the source code is detected. This is very similar to the way nodemon behaves as we learned during the first part of this course.



JSX

https://reactjs.org/docs/introducing-jsx.html

const element = <h1>Hello, world!</h1>;

This funny tag syntax is neither a string nor HTML. It is called JSX, and it is a syntax extension to JavaScript. We recommend using it with React to describe what the UI should look like. JSX may remind you of a template language, but it comes with the full power of JavaScript. JSX produces React "elements". We will explore rendering them to the DOM.



App tsx

We are now going to take a look to the source code of the src/App.tsx file that was generated by the create-react-app command:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.tsx</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

export default App;


Let's ignore the entire code snippet and only focus on the following section for now:

<p>
  Edit <code>src/App.tsx</code> and save to reload.
</p>


This is the sentence that was displayed on the screen when we started the local web server. We are going to change the preceding code snippet to something like the following:

<p>
  Hello React!
</p>


Then, if we save the changes to the file, our browser will automatically be refreshed and we will see thenew sentence displayed on the screen.



index css

In this file we can set some global attributes. For example, let's define a different padding for the body:

src/index.css
body {
  margin: 0;
  padding: 30px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}



index tsx and index html

index.tsx is the application entry point:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();


In general, React generates html. So, to render («render» means "to execute the code and generate the html") the App in the client site we need:

import ReactDOM from 'react-dom';

react-dom have a method call render (ReactDom.render) that is the method that executes the code and generates the html:

ReactDOM.render(<App />, document.getElementById('root'));

In the previous line, we are basically saying, we want to render the <App /> component, and we want to render it into document.getElementById('root') (so into the Element of the Document with ID 'root'). In other words, there is a html page, and in this page there is an element with ID root and inside this element is where the React App is going to be run. You can check this by going to the public/index.html file. Notice in this file:

  • <div id="root"> <d/iv>  : Inside this <div> is where ReactDOM.render is going to place the html generated by React.
  • <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />  : That line is going to load the manifest.json, which is going to render the React Applicatino into the above <div> .


We said that we are creating a single web page App because what the server send to the client is only one page, which is that index.html page. Then, the Reanc Application can have as many pages as we want, but they are all generated in the client site.


Let's make some change in index.tsx:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';


const element = <h1>Helo</h1>;

ReactDOM.render(element, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();


With the above changes, what is going to be rendered into document.getElementById('root') is element. So the Application in your web browser is going to show only "Hello".

That was just an example, but what we usually do is to create components.



Components - Example 1

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';


class MyComponent extends React.Component {
    public render(){
        const element = <h1>Hello</h1>;
        return element;
    }
}

ReactDOM.render(<MyComponent/>, document.getElementById('root'));

This component becomes a html tag, and that is why we can render it this way: <MyComponent/>. Now we can use our <MyComponent/> html tag as many times as we want and so reuse code.

In the above example we render our <MyComponent/> tag, but we can also use standard html tags, <div> for example. We can do something like this:

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';


class MyComponent extends React.Component {
    public render(){
        const element = <h1>Hello</h1>;
        return element;
    }
}

ReactDOM.render(
    <div>
        <MyComponent/>
        <MyComponent/>
        <MyComponent/>
        <MyComponent/>
    </div>,
    document.getElementById('root')
);


Let's do a more realistic example. Suppose that we want to create a Header with a particular style. In a company, for example, we want to make sure that every time someone need a header, this is going to be the same. We don't want the header in one page to look one way and in a different way in another page. We can for example define the CSS style of our component this way:

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';


class MyComponent extends React.Component {
    public render(){
        const style: React.CSSProperties = {
            borderBottom: "1px solid blue"
        };
        const element = <h1 style={style}>Hello</h1>;
        return element;
    }
}

ReactDOM.render(
    <div>
        <MyComponent/>
        <MyComponent/>
        <MyComponent/>
        <MyComponent/>
    </div>,
    document.getElementById('root')
);

It is very important to notice that:

  • The value of the variable style (that is an object) is going to be the CSS of our component.
  • React.CSSProperties is the type of the variable (style) we are creating. Remenber that in TypeScript we need to define the type of the variable.
  • Also, notice that in CSS, the attribute we are defining would be border-bottom, but in react (JSX) is borderBottom.


We can also define argument in our components. Let's see how to do so:

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';


interface HeaderProps {
    title: string;
}

interface HeaderState {

}


class Header extends React.Component<HeaderProps, HeaderState> {
    public render(){
        const style: React.CSSProperties = {
            borderBottom: "1px solid blue"
        };
        const element = <h1 style={style}>{this.props.title}</h1>;
        return element;
    }
}


ReactDOM.render(
    <div>
        <Header title="Header 1"/>
        <Header title="Header 2"/>
        <Header title="Header 3"/>
        <Header title="Header 4"/>
    </div>,
    document.getElementById('root')
);


Notice that we have also changed the name of our component from «MyComponent» to «Header» (just to make it a bit more realistic).


Now, we just should move our Header component into a separate file in a folder called «components»:

src/components/header/header.tsx
import React from 'react';


interface HeaderProps {
    title: string;
}

interface HeaderState {

}


export class Header extends React.Component<HeaderProps, HeaderState> {
    public render(){
        const style: React.CSSProperties = {
            borderBottom: "1px solid blue"
        };
        const element = <h1 style={style}>{this.props.title}</h1>;
        return element;
    }
}

Notice the «export» that have to be added before the «class».


Now we have to export our Header component in «index.tsx»:

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {Header } from "./components/header/header"


ReactDOM.render(
    <div>
        <Header title="Header 1"/>
        <Header title="Header 2"/>
        <Header title="Header 3"/>
        <Header title="Header 4"/>
    </div>,
    document.getElementById('root')
);


Let's add another properties to our component.

src/components/header/header.tsx
import React from 'react';


interface HeaderProps {
    size: number;
    title: string;
    enableBaclgroud: boolean;
}

interface HeaderState {

}


export class Header extends React.Component<HeaderProps, HeaderState> {
    public render(){
        const style: React.CSSProperties = {
            borderBottom: "1px solid blue",
            backgroundColor: this.props.enableBaclgroud ? "red" : "none"
        };
        switch(this.props.size){
            case 1:
                return <h1 style={style}>{this.props.title}</h1>;
            case 2:
                return <h2 style={style}>{this.props.title}</h2>;
            case 3:
                return <h3 style={style}>{this.props.title}</h3>;
            case 4:
                return <h4 style={style}>{this.props.title}</h4>;
            default:
                return new Error("Invalid size");
        }
    }
}

Notice the line:

backgroundColor: this.props.enableBaclgroud ? "red" : "none"

The above line is saying:

if this.props.enableBaclgroud = true: backgroundColor is «red»,
if false: «none»


Then,

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {Header } from "./components/header/header"


ReactDOM.render(
    <div>
        <Header size={1} title="Header 1" enableBaclgroud={true}  />
        <Header size={1} title="Header 2" enableBaclgroud={false} />
        <Header size={1} title="Header 3" enableBaclgroud={true}  />
        <Header size={1} title="Header 4" enableBaclgroud={false} />
    </div>,
    document.getElementById('root')
);


Now suppose that we want to implement a more difficult code. If we want, for example, implement an «if, else» inside a JSX block (inside the <h1> </h1> , for example):

src/components/header/header.tsx
import React from 'react';


interface HeaderProps {
    size: number;
    title: string;
    enableBaclgroud: boolean;
}

interface HeaderState {

}


export class Header extends React.Component<HeaderProps, HeaderState> {
    public render(){
        const style: React.CSSProperties = {
            borderBottom: "1px solid blue",
            backgroundColor: this.props.enableBaclgroud ? "red" : "none"
        };
        switch(this.props.size){
            case 1:
                return <h1 style={style}>
                    {
                        (() => {
                            if (true) {
                                return "Hello";
                            } else {
                                return "World";
                            }
                        })()
                    }
                    {this.props.title}
                </h1>;
            case 2:
                return <h2 style={style}>{this.props.title}</h2>;
            case 3:
                return <h3 style={style}>{this.props.title}</h3>;
            case 4:
                return <h4 style={style}>{this.props.title}</h4>;
            default:
                return new Error("Invalid size");
        }
    }
}


Another and better aproach is to create another method:

src/components/header/header.tsx
import React from 'react';


interface HeaderProps {
    size: number;
    title: string;
    enableBaclgroud: boolean;
}

interface HeaderState {

}


export class Header extends React.Component<HeaderProps, HeaderState> {
    public render(){
        const style: React.CSSProperties = {
            borderBottom: "1px solid blue",
            backgroundColor: this.props.enableBaclgroud ? "red" : "none"
        };
        switch(this.props.size){
            case 1:
                return <h1 style={style}>
                    {this._renderConditional()}
                    {this.props.title}
                </h1>;
            case 2:
                return <h2 style={style}>{this.props.title}</h2>;
            case 3:
                return <h3 style={style}>{this.props.title}</h3>;
            case 4:
                return <h4 style={style}>{this.props.title}</h4>;
            default:
                return new Error("Invalid size");
        }
    }


    private _renderConditional(){
        if (true) {
            return "Hello";
        } else {
            return "World";
        }
    }

}

The _ in _renderConditional() is just a convention that is usually used in many languages to make clear that we are talking about a private element.


Let's create another component:

src/components/panel/panel.tsx
import React from 'react';


interface PanelProps {
    header: string;
    body:   string;
    footer: string;
}

interface PanelState {

}


export class Panel extends React.Component<PanelProps, PanelState> {
    public render(){
        const panelStyle: React.CSSProperties = {
            border: "1px solid #333333",
            background: "blue",
            borderRadius: "5px"
        };
        const headerStyle: React.CSSProperties = {
            padding: "5px",
            borderBottom: "1px solid #333333",
        };
        const bodyStyle: React.CSSProperties = {
            padding: "5px",
            borderBottom: "1px solid #333333",
        };
        const footerStyle: React.CSSProperties = {
            padding: "5px",
            borderBottom: "1px solid #333333",
        };
        return <div style={panelStyle}>
            <div style={headerStyle}>{this.props.header}</div>
            <div style={bodyStyle}>{this.props.body}</div>
            <div style={footerStyle}>{this.props.footer}</div>
        </div>;
    }

}


src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header"
import {Panel } from "./components/panel/panel"


ReactDOM.render(
    <div>
        <Header size={1} title="Header 1" enableBaclgroud={true}  />
        <Header size={1} title="Header 2" enableBaclgroud={false} />
        <Header size={1} title="Header 3" enableBaclgroud={true}  />
        <Header size={1} title="Header 4" enableBaclgroud={false} />
        <Panel  header={"The header"} body={"The body"} footer={"The footer"} />
    </div>,
    document.getElementById('root')
);



Components - Example 2

src/components/header/header.tsx
import React from 'react';


interface HeaderProps {
    label: string;
    color?: string;
}

interface HeaderState {

}


export class Header extends React.Component<HeaderProps, HeaderState> {
    public render(){
        const style: React.CSSProperties = {
            fontFamily: `Rubik,Lato,"Lucida Grande","Lucida Sans Unicode",Tahoma,Sans-Serif`,
            fontSize: "56px",
            fontWeight: 700,
            color: this.props.color !== undefined ? this.props.color : "#000000",
            borderBottom: "3px solid"
        };
        return <div>
            <h1 style={style}>{this.props.label}</h1>
        </div>
    }

    
}

In the example above, we have defined an optional property for the Header component. Notice that «color?» has been defined with a «?» at the end. This means that we can user (or not) the color property when using the Header component. Also notice that, when we declare an optional property, this need to be defined using an «else, if» block. So this way we define what happens when the property is used and when is not used. This was made in the above code this way:

color: this.props.color !== undefined ? this.props.color : "#000000",

This line means: in case the property is defined, this.props.color : "#000000",


src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header";
import {Panel } from "./components/panel/panel";


ReactDOM.render(
    <div>
        <Header label="My Header 1" />
        <Panel
            header="The header"
            body="The body"
            footer="The footer"
        />

    </div>,
    document.getElementById('root')
);


Let's now create another component:

src/components/listview/listview.tsx
import React from 'react';


interface ListviewProps {
    items: string[];
}

interface ListviewState {

}


export class Listview extends React.Component<ListviewProps, ListviewState> {
    public render(){
        if (this.props.items.length < 1){
            return <div>There if no items</div>;
        } else {
            return <div>{this.props.items}</div>;
        }
    }

}



Mapping data into JSX elements

This is how in react we transform data into JSX elements.


Suppose that we have an array of number (data):

var arr = [1, 2, 3, 4, 5];

We can use the «map» function of the «arr» class to make change in our array:

var arr2 = arr.map(function (n) {return n * 2});

In this caes «arr2» is going to contain [2, 4, 6, 8, 10]


We can also transform an array of number into an array of strings: ["1", "2", "3", "4", "5"]

var arr3 = arr.map(function (n) {return n.toString()});


But another more interested thing that we can do, is to transform the array of number into an array of react elements (JSX elements). For example:

var arr4 = arr.map(function (n) {return <li>{n}</li>});


The last code ins going to return:

[<li>1</li>, <li>2</li>, <li>3</li>, <li>4</li>, <li>5</li>]


So, we are going to use the «map» function into our Listview component to generate a list:

src/components/listview/listview.tsx
import React from 'react';


interface ListviewProps {
    items: string[];
}

interface ListviewState {

}


export class Listview extends React.Component<ListviewProps, ListviewState> {
    public render(){
        if (this.props.items.length < 1){
            return <div>There if no items</div>;
        } else {
            return <ul>
                {this.props.items.map(function (item) {
                    return <li>{item}</li>
                })}
            </ul> ;
        }
    }

}


src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header2";
import {Panel } from "./components/panel/panel";
import {Listview } from "./components/listview/listview";


ReactDOM.render(
    <div>
        <Header label="My Header 1" />
        <Panel
            header="The header"
            body="The body"
            footer="The footer"
        />
        <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
    </div>,
    document.getElementById('root')
);



A component inside another component

Let's create a card component:

src/components/card/card.tsx
import React from 'react';


interface CardProps {
    child: JSX.Element;
}

interface CardState {

}


export class Card extends React.Component<CardProps, CardState> {
    public render(){
        const style: React.CSSProperties = {
            border: "1px solid #eeeeee",
            boxShadow: "0 10px 6px -6px #777"
        };
        return <div style={style}>
            {this.props.child}
        </div>
    }


}

Notice that we have passed a property of type JSX.Element. Now, we can pass another component to our Card component:

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header2";
import {Panel } from "./components/panel/panel";
import {Listview } from "./components/listview/listview";
import {Card } from "./components/card/card";


ReactDOM.render(
    <div>
        <Header label="My Header 1" />
        <Panel
            header="The header"
            body="The body"
            footer="The footer"
        />
        <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        <Card child={<Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />}/>
    </div>,
    document.getElementById('root')
);


However, there is a standard and better way to do so:

We know that there are tow ways of open and close an element. We can do is this way:

<Header/>
<Header label="My Header 1" />

Or we can do it this way:

<Card>
</Card>

In the last case we can put something in the medle, for example:

<Card>
    <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
</Card>

which is the standard way to pass an element inside another element. In this case we don't need to define a «clild» property in the «Props» interface; and we can use the «children» special property (that don't need to be defined in the «Props» interface because is defined by default) to refer to this child element. Let's see that in code:

src/components/card/card.tsx
import React from 'react';


interface CardProps {

}

interface CardState {

}


export class Card extends React.Component<CardProps, CardState> {
    public render(){
        const style: React.CSSProperties = {
            border: "1px solid #eeeeee",
            boxShadow: "0 10px 6px -6px #777",
            marginBottom: "30px"
        };
        return <div style={style}>
            {this.props.children}
        </div>
    }


}


src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header2";
import {Panel } from "./components/panel/panel";
import {Listview } from "./components/listview/listview";
import {Card } from "./components/card/card";


ReactDOM.render(
    <div>
        <Header label="My Header 1" />
        <Panel
            header="The header"
            body="The body"
            footer="The footer"
        />
        <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        <Card>
            <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        </Card>
    </div>,
    document.getElementById('root')
);



States

If we want, for example, an element to be collapsed or expanded, we have to use the «State Interface» of the component:

...

interface CardState {
    isCollapsed: boolean;
}
...


Let's see an example:

src/components/card/card.tsx
import React from 'react';


interface CardProps {

}

interface CardState {
    isCollapsed: boolean;
}


export class Card extends React.Component<CardProps, CardState> {
    public constructor(props: CardProps){
        super(props);
        this.state = {
            isCollapsed: false
        }
    }

    public render(){
        const style: React.CSSProperties = {
            border: "1px solid #eeeeee",
            boxShadow: "0 10px 6px -6px #777",
            marginBottom: "30px"
        };
        return <div style={style}>
            <button onClick={() => this._toggle()}>
                {this.state.isCollapsed ? "Open" : "Close"}
            </button>
            {/* Now we need to do an «if - else» to show or not the 
                 childre depending on the value of «isCollapsed» 
                 We can do an «if - else» inside the JSX block in 
                 different ways: */ }
            {/* 1. Using a function that calls itself: */}
            {
                (() => {
                    if (this.state.isCollapsed){
                        return null;
                    } else {
                        return this.props.children;
                    }
                })()
            }
            {/* 2. Defining the block in one line this way: */}
            {/* {this.state.isCollapsed ? null : this.props.children} */}
            {/* 3. Creating another method: */}
            {/* {this._renderChildren()} */}
        </div>
    }

    private _renderChildren(){
        if (this.state.isCollapsed) {
            return null;
        } else {
            return this.props.children;
        }
    }

    private _toggle(){
        // this.state.isCollapsed = false; // This way is not going to work. this has to be done useing «setState», because «setState» rerender the html
        this.setState({isCollapsed: !this.state.isCollapsed });
    }


}


From the last code, notice:

  • When we use states, we also need to define the initial state. To do so, we need to add a «constructor» to our class:
public constructor(props: CardProps){
    // We first need to call the constructor of the superclass and pass it as a parameter a variable of type «CardProps»:
    super(props); 
    // Then we have to define the initial state of the attributes in the «state interface»:
    this.state = {
        isCollapsed: false
    }
}


  • Then, we need to create a method that is going to chage the value of the «state attribute» (in our example «isCollapsed») and is going to rerender the html when the state changes:
private _toggle(){
    // We are setting «isCollapsed» equal to the opposite (!) of its value
    // The «setState» method is in charge of render the application. If the application is not rendered, we will never see the changes in the page.
    this.setState({isCollapsed: !this.state.isCollapsed });
}


  • Then, we define a button that execute the «_toggle» method on click:
<button onClick={() => this._toggle()}>
    {this.state.isCollapsed ? "Open" : "Close"}
</button>

Every time we click in this button, the value of «isCollapsed» is changed to its opposite and the application is rendered.


  • Finally, we define what is going to happen depending on the value of «isCollapsed». In our example, we need to do an «if - else» block so:
if (this.state.isCollapsed == true){
    return null;                // Nothing in the page
} else {
    return this.props.children; // The child component
}

Only when the value of «isCollapsed» is false, we are going to show the child component in the page.


There are different ways to do an «if - else» inside a JSX block:

1. Using a function that calls itself:
(() => {
    if (this.state.isCollapsed){
        return null;
    } else {
        return this.props.children;
    }
})()


2. Defining the block in one line this way:
{this.state.isCollapsed ? null : this.props.children}


3. Creating another method:
// Inside the JSX block:
{this._renderChildren()}

// Outside the JSX block:
private _renderChildren(){
    if (this.state.isCollapsed) {
        return null;
    } else {
        return this.props.children;
    }
}



Components - Fetching data from the server


Example 1

We are going to create a «.json» file that is going to simulate data that come from the server. So, we are going to create a folder inside the «public» folder called «data» and inside this folder our «.json» file. We are creating thsi «.json» file because this is the format in which data from the server will come.

public/data/cats.json
[
    "https://images.pexels.com/photos/257540/pexels-photo-257540.jpeg",
    "https://images.unsplash.com/photo-1518791841217-8f162f1e1131",
    "https://news.nationalgeographic.com/content/dam/news/2018/05/17/you-can-train-your-cat/02-cat-training-NationalGeographic_1484324.ngsversion.1526587209178.adapt.1900.1.jpg",
    "https://images.unsplash.com/photo-1532386236358-a33d8a9434e3",
    "https://ichef.bbci.co.uk/images/ic/720x405/p0517py6.jpg",
    "https://cdn.cnn.com/cnnnext/dam/assets/150324154010-04-internet-cats-restricted-super-169.jpg"
]


Now, let's create a «src/pages» folder and inside it our first page. We will start by a hard code example. In this example, nothing comes from the server nor from public/data/cats.json, but this way we can understand some aspects:

src/pages/cats.tsx
import React from 'react';

import {Carousel } from "../components/carousel/carousel";

interface CatsProps {

}

interface CatsState {

}


export class Cats extends React.Component<CatsProps, CatsState> {
    public render(){
        const imgStyle: React.CSSProperties = { width: "100px", height: "100px"};
        return (
            <Carousel
                items={[
                    <img style={imgStyle} src="https://images.pexels.com/photos/257540/pexels-photo-257540.jpeg" />,
                    <img style={imgStyle} src="https://images.unsplash.com/photo-1518791841217-8f162f1e1131" />,
                    <img style={imgStyle} src="https://ichef.bbci.co.uk/images/ic/720x405/p0517py6.jpg" />
                ]}
                startInIndex={0}
            />
        );
    }

}

It is important to notice that, in React, a page is also a component. We make the different only because pages are not going to be reusable. We are not going to use a page inside a component or inside another pages. But technically, React doesn't see any different between components and pages.


src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header2";
import {Panel } from "./components/panel/panel";
import {Listview } from "./components/listview/listview";
import {Card } from "./components/card/card";
import {Cats} from "./pages/cats";


ReactDOM.render(
    <div>
        <Header label="My Header 1" />
        <Panel
            header="The header"
            body="The body"
            footer="The footer"
        />
        <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        <Card>
            <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        </Card>
        <Cats/>
    </div>,
    document.getElementById('root')
);


Now, we are going to replace the hard code in pages/cats.tsx that includes the images for a fetch code that will take the images from the public/data/cats.json file (which is simulating the data that comes from the server):

First we need to create a function that is going to fetch the data from the file:

async function getData(){
    const response = await fetch("/data/cats.json");
    const json = await response.json();
    return json;
}

Notice that await has to be used because we are fetching data from the server, and we don't know how long the server is going to take to response. Also, if we use await we have to use an 'async function.


We also need to add a «state» that is going to represent the data:

interface CatsState {
    // items is type string[] or null:
    items: string[] | null;
}


Also, as we already know, when we use states, we need to use a constructor to set the initial value of the state:

public constructor(props: CatsProps){
    super(props);
    this.state = {
        items: null
    };
}


Now we need at some point call the getData() method to load the data into the page. To do so, we are going to use a special Lifecycle method called componentDidMount(). They are called like this because they are executed at a specific point of the Lifecycle of a component. The componentDidMount() it's going to be called after the component has rendered. To know more about the Lifecycle take a look at this page: https://reactjs.org/docs/state-and-lifecycle.html

So, we need to call the getData() function inside the componentDidMount() function; but we need to place getData() into a function that calls itself. We also have to call setState() to render the application when items changes:

public componentDidMount(){
    (async () => {
        const data = await getData();
        this.setState({items: data});
    })();
}

Notice that we could have called the getData and setState directly in the constructor, but this is not the correct way to do it because we don't know how long will take to have the data loaded, so the application could be rendered before the data have been loaded.


Now, in the variable data we have an array of url, but what I need is an array of img:


.
.
.

So we need to map our data into JSX elements (go from an array of url to an array of imgs) using the map function as we learned before:

this.state.items.map((item, indexItem) => {
    return (
        <img style={imgStyle} src={item} />
    );
})


Let's talk a little about the Lifecycle to understand what is happening in our application:

  1. The component is created. So the constructor is called: in our example we set the initial value of the items state as null.
  2. Then, the component is going to be rendered. So the first time is rendered, items will be null and the message «Loding...» will be displayed on the page.
  3. After the component is rendered, componentDidMount() is called:
  • We fetch the data using the getData() function.
  • We call setState() and set items = data, then the component is going to be rendered again and the data will be shown in the page.


src/pages/cats.tsx
import React from 'react';

import {Carousel } from "../components/carousel/carousel";

interface CatsProps {

}

interface CatsState {
    items: string[] | null;
}


export class Cats extends React.Component<CatsProps, CatsState> {
    public constructor(props: CatsProps){
        super(props);
        this.state = {
            items: null
        };
    }
    public componentDidMount(){
        (async () => {
            const data = await getData();
            this.setState({items: data});
        })();
    }
    public render(){
        const imgStyle: React.CSSProperties = { width: "100px", height: "100px"};
        if (this.state.items === null){
            return <div>Loding...</div>
        } else {
            return (
                <Carousel
                    items={
                        this.state.items.map((item, indexItem) => {
                            return (
                                <img style={imgStyle} src={item} />
                            );
                        })
                    }
                    startInIndex={0}
                />
            );
        }
    }
}

async function getData(){
    const response = await fetch("/data/cats.json");
    const json = await response.json();
    return json;
}



Example 2

To keep practicing about it, we can use this web side: http://jsonplaceholder.typicode.com/todos:


src/pages/todos.tsx
import React from 'react';

import {Listview } from "../components/listview/listview";


interface Todo {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
}

interface TodosProps {

}

interface TodosState {
    todos: Todo[] | null;
}


export class Todos extends React.Component<TodosProps, TodosState> {
    public constructor(props: TodosProps){
        super(props);
        this.state = {
            todos: null
        };
    }
    public componentDidMount(){
        (async () => {
            const data = await getData();
            this.setState({ todos: data });
        })();
    }
    public render(){
        const imgStyle: React.CSSProperties = { width: "100px", height: "100px"};
        if (this.state.todos === null){
            return <div>Loding...</div>
        } else {
            return (<Listview
                    items={
                        this.state.todos.map((todo) => {
                            return todo.userId.toString() + "  " + todo.title;
                        })
                    }
                />
            );
        }
    }
}

async function getData(){
    const response = await fetch("http://jsonplaceholder.typicode.com/todos");
    const json = await response.json();
    return json as Todo[];
}

In the previous code, note that the property «items» of <Listview> have to be a string because in listview.tsx we defined the type of «items» as a string:

src/components/listview/listview.tsx
.
.
.
interface ListviewProps {
    items: string[];
}
.
.
.


that is why here we have to return a string:

src/pages/todos.tsx
.
.
.
items={
    this.state.todos.map((todo) => {
        return todo.userId.toString() + "  " + todo.title;
    })
}
.
.
.

but let's make a change in listview.tsx. We are going to set the type of «items» as JSX.Element[]. This way listview.tsx would looks like this:

src/components/listview/listview2.tsx
import React from 'react';


interface Listview2Props {
    items: JSX.Element[];
}

interface Listview2State {

}


export class Listview2 extends React.Component<Listview2Props, Listview2State> {
    public render(){
        if (this.props.items.length < 1){
            return <div>There if no items</div>;
        } else {
            return <ul>
                {this.props.items.map(function (item) {
                    return <li>{item}</li>
                })}
            </ul> ;
        }
    }

}

Now, we are allowed to return any kind of JSX element. We can for example do something like this:

src/pages/todos.tsx
import React from 'react';

import {Listview } from "../components/listview/listview";
import {Listview2 } from "../components/listview/listview2";


interface Todo {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
}

interface TodosProps {

}

interface TodosState {
    todos: Todo[] | null;
}


export class Todos extends React.Component<TodosProps, TodosState> {
    public constructor(props: TodosProps){
        super(props);
        this.state = {
            todos: null
        };
    }
    public componentDidMount(){
        (async () => {
            const data = await getData();
            this.setState({ todos: data });
        })();
    }
    public render(){
        const imgStyle: React.CSSProperties = { width: "100px", height: "100px"};
        if (this.state.todos === null){
            return <div>Loding...</div>
        } else {
            return (<Listview2
                        items={
                            this.state.todos.map((todo) => {
                                // To be able to return a JSX element, the type of
                                // items have to be JSX.Element[], as was defined in <Listview2>
                                // If we use <Listview> we have to return a string.
                                return <div>
                                    <input type="checkbox" checked={todo.completed} />
                                    {todo.title}
                                </div>;
                            })
                        }
                />
            );
        }
    }
}

async function getData(){
    const response = await fetch("http://jsonplaceholder.typicode.com/todos");
    const json = await response.json();
    return json as Todo[];
}
src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import {Header } from "./components/header/header2";
import {Panel } from "./components/panel/panel";
import {Listview } from "./components/listview/listview";
import {Card } from "./components/card/card";
import {Cats} from "./pages/cats";
import {Todos} from "./pages/todos";


ReactDOM.render(
    <div>
        <Header label="My Header 1" />
        <Panel
            header="The header"
            body="The body"
            footer="The footer"
        />
        <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        <Card>
            <Listview items={["Titanic", "Interview with the vampire", "The Lion King"]} />
        </Card>
        <Cats/>
        <Todos/>
    </div>,
    document.getElementById('root')
);



Routing in React

Routing is the mechanism by which requests (as specified by a URL and HTTP method) are routed to the code that handles them.

In a traditional web application, Routing used to be file based and very simple: if you put the file foo/about.html on your website, you would access it from the browser with the path yourdomain/foo/about.html.

In modern web application, URL Routing allows you to configure an application to accept request URLs that do not map to physical files.

In this sense, Routing enables you to use a specific path for your website. For example if you had a products website, the URL structure might be like this: www.example.com/products/electronics/monitiors

This URL does not need to be linked to a specific file or directory, as products/electronics might not even exist.

A URL like the one above is much easier to understand than a URL like www.example.com/products.aspx?id=250.


By default, React doesn't include a Routing module. Actually, the base React Library is very small (a diferencia de Angular where everything is built-in). Therefore, we need to add an additional library to Routing. There are many different Routing libraries that can be implemented in React. Take a look at some of the most important: https://reactjs.org/community/routing.html



react-router

The Routing library that we are going to implement is react-router https://github.com/ReactTraining/react-router

More specifically, we are going to use the react-router-dom package: DOM bindings for React Router. We need this one because we are going to do Routing in the browser: https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom



react-router-dom installation

npm install react-router-dom         (instalación global)
npm install --save react-router-dom  (instalación local, sólo en el directorio en donde estamos trabajando. No debería ser necesario si lo instalamos global)

and because we are working with TypeScript, we also need:

npm install @types/react-router-dom



Documentation and tutorials

https://reacttraining.com/react-router/web/guides/quick-start

https://blog.pshrmn.com/simple-react-router-v4-tutorial/



Configuring the Routing in our App

The routing is a global configuration of our application. We are going to configure it in index.jsx. In previous sections, we have been rendering our components and pages including then in index.jsx, but that is not the way a real Web Application would work.

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

import {BrowserRouter, Switch, Route} from 'react-router-dom';

import {Todos} from './pages/todos';
import {Cats} from './pages/cats';


ReactDOM.render(
    <BrowserRouter>
        <Switch>
            <Route exact path="/" component={Todos} />
            <Route exact path="/cats" component={Cats} />
        </Switch>
    </BrowserRouter>,
    document.getElementById('root')
);

Notice:

  • import {BrowserRouter} from 'react-router-dom';
  • Now, our root component is <BrowserRouter>
  • It is very important to notice that, when we change the page, the application is not going to the server to fetch a new page, this is happening in the client site, and that is why is called a Single Page Web Application.


We can also do something like this:

src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

import {BrowserRouter, Switch, Route, Link} from 'react-router-dom';

import {Todos} from './pages/todos';
import {Cats} from './pages/cats';


const headerStyle: React.CSSProperties = {
    backgroundColor: "#333333",
    height: "100px"
};

ReactDOM.render(
    <BrowserRouter>
        <div>
            <div style={headerStyle}>
                <Link style={{ color: "#ffffff"}} to="/">Todos</Link>
                <Link style={{ color: "#ffffff"}} to="/cats">Cats</Link>
            </div>
            <Switch>
                <Route exact path="/" component={Todos} />
                <Route exact path="/cats" component={Cats} />
            </Switch>
        </div>
    </BrowserRouter>,
    document.getElementById('root')
);
  • Notice that, when we click in a link (in «Cats», for example) and the URL changes, the <Cats> component is rendered, but not the whole page (not the top header that we have included). The only part that is rendered is what is inside the <Switch> component.



Login Form

The next page will be in charge of manage the login process:

src/pages/login.tsx
import React from 'react';
import { stringify } from 'querystring';


interface LoginProps {

}

interface LoginState {
    email: string;
    password: string
}


export class Login extends React.Component<LoginProps, LoginState> {
    public constructor(props: LoginProps){
        super(props)
        this.state = {
            email: "",
            password: ""
        };
    }
    public render(){
        return(
            <div>
                <input
                    type="text"
                    placeholder="Email"
                    onKeyUp={(e) => this._updateEmail((e as any).target.value)}
                />
                <input
                    type="password"
                    placeholder="Password"
                    onKeyUp={(e) => this._updatePassword((e as any).target.value)}
                />
                <button onClick={() => this._handlerSubmit()}>Submit</button>
            </div>
        );
    }
    private _updateEmail(email: string){
        // console.log(email);  // We can do this just to verify that the keys that are being typing into the form field are printed in the browser console.
        this.setState({email: email});
    }
    private _updatePassword(password: string){
        this.setState({password: password});
    }
    private _handlerSubmit() {
        (async () => {
            await getToken(this.state.email, this.state.password);
        })();
    }
}


async function getToken(email: string, password: string){
    const data = {
        email: email,
        password: password
    }
    const response = await fetch(
        "/api/v1/auth/login",
        {
            method: "POST",
            headers: {
                'Content-type': 'application/json'
            },
            body: JSON.stringify(data)
        }
    );
    const json = await response.json();
    // { token: "lasflsfooiwerojsdfosjfoijljosdifjosdfijsdfoijoweoierj" }
    return json.token;
}

Let's explain the above code:

  • We have defined two states: email and password


  • As we have already see, we need a constructor to define the initial values of the states.


  • Then, we define, inside the render method, the form that is going to be used to entered the data: The Email and Password fields and the Submit button.


  • To be able to take the Email and Password entered for the user in the form fields, We first need to be able to detect changes in the Email and Password form fields. To do so, we need to add to the <input> component, the «onKeyUp» parameter, so everytime we type a key, it triggers an event and we can use this event to call any particular method that we want to execute when the user types into the form fields. In this case, we want to call the _updateEmail() and _updatePassword() methods, which are in charge of update the email and password states, respectively.


  • It is important to notice that all this kind of events:
onClick={() => this._handlerSubmit()}
onKeyUp={() => this._updateEmail()}
onKeyPress
.
.
.
etc
they always take a function ( onClick={ () => this._handlerSubmit() } ) which is calling something, and this function always take an argument that is called the event (e). For example, we can write the onClick function this way: onClick={ (e) => this._handlerSubmit() }. In the case of the onClick, we can omit the event because we don't care which is the click event, whichever click is going to have the same effect; but in the case of onKeyUp we want to know which key has been pressed. Therefore, we have to write the onKeyUp this way:
onKeyUp={(e) => this._updateEmail((e as any).target.value)}
That way we can take the value of the event (in this case the key that has been typed). We could test it by writing the _updateEmail() function this way:
private _updateEmail(email: string){
    console.log(email);
}
and then, when we write into the form field, we can open the browser console and verify that the keys that we are typing are being printed in the console:
onKeyUp


  • So, we can see that everytime the user enters a new key into the form field, the values of the email and password states are updated. This way, when the user finished to enter the data into the form field, the values of the email and password states will be the whole string the user have entered.


  • We can then use the _handlerSubmit() and getToken() methods to send the Email and Password to the corresponding REST Web Services in charge of the login (http://localhost:8080/api/v1/auth/login in our case). This REST Web Service will take the Email and Password and will check if the information is valid according to the database. In case the information is valid, http://localhost:8080/api/v1/auth/login will return an auth token.


  • Now, let's analyses what happen when we press the submit button:
  • We go to our Web App in the brower (the explanation here is using GoogleChrome)
  • We input some data into the Email and Password fields and we press the Submit button


  • Let's open the Developer tools (Crl + Shift + i) and go to the Network tap:
  • Click in the htlm POST request we have just done to http://localhost:8080/api/v1/auth/login
  • Here we can see the details of the html request. In this section let's go to the Headers tap. Notice in the «Request Payload» section that the data entered into the form fields (Email and Password) has been sent:
Html request details.png
This is logical because our RES Web service is not running so we cannot reach http://localhost:3000/api/v1/auth/login


public/data/login.json

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTQ4NzY0MDIwfQ.4NVMemKu9s_h-r68gy-QKHjBtJSWpYPLjSPBwWGylbY"
}
this file is how the response would look like if it comes from http://localhost:3000/api/v1/auth/login in a real server.
We have to make some changes in the getToken() method to be able to simulate the response coming from a local file. In the case of this local file, we will just make a GET request to fetch the data from /data/login.json. Therefore, our getToken() method would be like this:

src/pages/login.tsx

.
.
.
async function getToken(email: string, password: string){
    const data = {
        email: email,
        password: password
    }
    const response = await fetch(
        "/data/login.json",
        {
            method: "GET",
            headers: {
                'Content-type': 'application/json'
            },
            // body: JSON.stringify(data) // No body for a GET request
        }
    );
    const json = await response.json();
    // { token: "lasflsfooiwerojsdfosjfoijljosdifjosdfijsdfoijoweoierj" }
    return json.token;
}
.
.
.


Now, if we analyse the request after pressing the Submit button, we can see the response of the request, which is the content of the login.json file:


Html request details from local file.png



Input validations with Joi

https://www.npmjs.com/package/joi

https://www.npmjs.com/package/@hapi/joi

https://www.npmjs.com/package/react-joi-validation


We can do input validations using the same library (joi) that we used in Node.js (Server side):

src/pages/login.tsx
import React from 'react';
import * as joi from "joi";


const credentialSchema =  {
    email: joi.string().email().required(),
    password: joi.string().min(3).max(30).required()
};


interface LoginProps {

}

interface LoginState {
    email: string;
    password: string
}


export class Login extends React.Component<LoginProps, LoginState> {
    public constructor(props: LoginProps){
        super(props)
        this.state = {
            email: "",
            password: ""
        };
    }
    public render(){
        return(
            <div>
                {/* We need to call the _renderValidationErrors method inside the render method: */}
                {this._renderValidationErrors()}
                <input
                    type="text"
                    placeholder="Email"
                    onKeyUp={(e) => this._updateEmail((e as any).target.value)}
                />
                <input
                    type="password"
                    placeholder="Password"
                    onKeyUp={(e) => this._updatePassword((e as any).target.value)}
                />
                <button onClick={() => this._handlerSubmit()}>Submit</button>
            </div>
        );
    }
    private _updateEmail(email: string){
        // console.log(email);  // We can do this just to verify that the keys that are being typing into the form field are printed in the browser console.
        this.setState({email: email});
    }
    private _updatePassword(password: string){
        this.setState({password: password});
    }
    private _handlerSubmit() {
        (async () => {
            await getToken(this.state.email, this.state.password);
        })();
    }
    private _renderValidationErrors(){
        // joi.validate takes a value and the Schema (validation rule)
        // In our case, the value is going to be an object with an Email and a Password
        // It returns a validation object
        const validationResults = joi.validate({
            email:   this.state.email,
            password: this.state.password},
            credentialSchema);
            // validationResults has an error property:
            if (validationResults.error){
                return <div>
                    {/* The error has a detail property, whici of type joi.ValidationErrorItem[] 
                        That we have to map into a JSX element: */}
                    {validationResults.error.details.map(d => <div>{d.message}</div>)}
                </div>;
            } else {
                return <div>OK!</div>;
            }
    }
}


async function getToken(email: string, password: string){
    const data = {
        email: email,
        password: password
    }
    const response = await fetch(
        "/api/v1/auth/login",
        {
            method: "POST",
            headers: {
                'Content-type': 'application/json'
            },
            body: JSON.stringify(data)
        }
    );
    const json = await response.json();
    // { token: "lasflsfooiwerojsdfosjfoijljosdifjosdfijsdfoijoweoierj" }
    return json.token;
}



Connecting the Front-end and the Back-end

  • In our React project, we need to configure the proxy property in package.json according to the domain and port where the back-end is running: "proxy": "http://localhost:8080",

package.json

{
  "name": "myreactapp",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:8080",
  "dependencies": {
    "@types/jest": "24.0.0",
    "@types/joi": "^14.3.2",
    "@types/node": "10.12.21",

  .
  .
  .


  • Then we need to start both projects: Node.js and React


  • To start the Node.js project, we go to the directory and run:
nodemon

This project is usually configured to run in port 8080.


Remember that before running the project, we need to start our postgres database and define the environment variables:

# To see the Postgres container:
sudo docker ps -a

# To start the Docker container:
sudo docker start 3234e2864a8e

# Set environment variables:
export DATABASE_HOST=localhost
export DATABASE_PORT=5432
export DATABASE_DB=twitterclone_db
export DATABASE_USER=postgres
export DATABASE_PASSWORD=secret

export AUTH_SECRET=miclaveprivada

If everything is ok, we'll be able to show all the links created using this url: http://localhost:8080/api/v1/links


  • Then we go to the React directory and run:
nmp run start

This project is usually configured to run in port 3000.


  • Let's do a first example in which the React App is going to fetch data using the REST Web Services that we created in the Back-end using Node.js:
We are going to do a very similar page to to the src/pages/todos.tsx page we previously created (which is taking data from this url: http://jsonplaceholder.typicode.com/todos) but now we are going to fetch data using our REST Web Service responsible to return all the links from our database: http://localhost:8080/api/v1/links:

src/pages/mylinks.tsx

import React from 'react';

import {Listview2 } from "../components/listview/listview2";


// We have called it LinkA to avoid confutions with the Link component 
// used in the routing process in index.tsx: import {BrowserRouter, Switch, Route, Link} from 'react-router-dom';
interface LinkA {
    id: number;
    url: String;
    title: string;
    reference_user: {
        id: number;
        email: string;
        password: string;
    };
}

interface MyLinksProps {

}

interface MyLinksState {
    linksA: LinkA[] | null;
}


export class MyLinks extends React.Component<MyLinksProps, MyLinksState> {
    public constructor(props: MyLinksProps){
        super(props);
        this.state = {
            linksA: null
        };
    }
    public componentDidMount(){
        (async () => {
            const data = await getData();
            this.setState({ linksA: data });
        })();
    }
    public render(){
        const imgStyle: React.CSSProperties = { width: "100px", height: "100px"};
        if (this.state.linksA === null){
            return <div>Loding...</div>
        } else {
            return (<Listview2
                        items={
                            this.state.linksA.map((l) => {
                                // To be able to return a JSX element, the type of
                                // items have to be JSX.Element[], as was defined in <Listview2>
                                // If we use <Listview> we have to return a string.
                                return (
                                    <div>
                                        <ul>
                                            {l.url}
                                        </ul>
                                        <ul>
                                            {l.reference_user.email}
                                        </ul>
                                    </div>
                                );
                            })
                        }
                />
            );
        }
    }
}

async function getData(){
    const response = await fetch("/api/v1/links");
    const json = await response.json();
    return json as LinkA[];
}

The url of our REST Web Service have to be entered this way: const response = await fetch("/api/v1/links");

If we to like this: const response = await fetch("http://localhost:8080/api/v1/links"); we'll have an error. Like this it doesn't work because if you have another port we'll be trying to share resources between two different domains, which is not allowed. This is way we had to configure the proxy in the package.json file, which redirect the request to the correct server.

Actually, notice that if we review the details of the html request in the Web Browser > Developer tools (Crl + Shift + i) we'll see that the Request URL is show to be from port 3000 (http://localhost:3000/api/v1/links) even if our REST Web Service is running in port 8080. That is like this because we are actually sending the request to the 3000 but the proxy is redirecting it to the 8080:


Html request details2.png



Create a Login Form










Headers- NavBar

Very nice: https://codesandbox.io/s/48y8zyz46w?from-embed


https://react-bootstrap.github.io/components/navbar/

https://react-bootstrap.github.io/components/navs/

https://www.npmjs.com/package/react-bootstrap

https://www.npmjs.com/package/bootstrap-4-react

https://www.npmjs.com/package/@types/react-bootstrap

https://getbootstrap.com/docs/4.1/examples/


To make Bootstrap Navbar and Routing working together:

https://stackoverflow.com/questions/54843302/reactjs-bootstrap-navbar-and-routing-not-working-together



Icons

https://www.npmjs.com/package/react-icons :


https://mdbootstrap.com/docs/react/content/icons-list/


https://material-ui.com/components/icons/



More about Components - Taken from the React official documentation

https://reactjs.org/docs/react-component.html

React lets you define components as classes or functions. Components defined as classes currently provide more features which are described in detail on this page. To define a React component class, you need to extend React.Component:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

The only method you must define in a React.Component subclass is called render(). All the other methods described on this page are optional.



Components and Props

Para probar códigos: https://codepen.io/pen

https://reactjs.org/docs/components-and-props.html

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components.

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called "props") and return React elements describing what should appear on the screen.



Function and Class Components

The simplest way to define a component is to write a JavaScript function:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

This function is a valid React component because it accepts a single props (which stands for properties) object argument with data and returns a React element. We call such components function components because they are literally JavaScript functions.


You can also use an ES6 class to define a component:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}


The above two components are equivalent from React's point of view.


Classes have some additional features that we will discuss in the next sections. Until then, we will use function components for their conciseness.



Rendering a Component

Previously, we only encountered React elements that represent DOM tags:

const element = <div />;

However, elements can also represent user-defined components:

const element = <Welcome name="Sara" />;

When React sees an element representing a user-defined component, it passes JSX attributes to this component as a single object. We call this object "props".

For example, this code renders "Hello, Sara" on the page:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Let's recap what happens in this example:

  • We call ReactDOM.render() with the <Welcome name="Sara" /> element.
  • React calls the Welcome component with {name: 'Sara'} as the props.
  • Our Welcome component returns a <h1>Hello, Sara</h1> element as the result.
  • React DOM efficiently updates the DOM to match <h1>Hello, Sara</h1>.


Note: Always start component names with a capital letter. React treats components starting with lowercase letters as DOM tags. For example, <div /> represents an HTML div tag, but <Welcome /> represents a component and requires Welcome to be in scope. To learn more about the reasoning behind this convention, please read JSX In Depth.



Composing Components

Components can refer to other components in their output. This lets us use the same component abstraction for any level of detail. A button, a form, a dialog, a screen: in React apps, all those are commonly expressed as components.

For example, we can create an App component that renders Welcome many times:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);



Extracting Components

Don’t be afraid to split components into smaller components.

For example, consider this Comment component:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}


This component can be tricky to change because of all the nesting, and it is also hard to reuse individual parts of it. Let's extract a few components from it.

First, we will extract Avatar:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

The Avatar doesn't need to know that it is being rendered inside a Comment. This is why we have given its prop a more generic name: user rather than author.

We recommend naming props from the component’s own point of view rather than the context in which it is being used.


We can now simplify Comment a tiny bit:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}


Next, we will extract a UserInfo component that renders an Avatar next to the user's name:

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}


This lets us simplify Comment even further:

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}


Extracting components might seem like grunt work at first, but having a palette of reusable components pays off in larger apps. A good rule of thumb is that if a part of your UI is used several times (Button, Panel, Avatar), or is complex enough on its own (App, FeedStory, Comment), it is a good candidate to be a reusable component.