Notes on React Learning

Notes on React Learning
Logo of React.

Following the tutorial


Source: React Tutorial for Beginners


Two ways to create a React App:

  1. Create React APP (CRA) (by React team)
  2. Vite (faster with smaller bundle size)

Create App:

  1. Install packages and create project
npm create vite@4.1.0
  1. Select framework:
Vanilla -- JavaScript without third-party tools
React
...
  1. Type the following code:
cd react-app
npm i  // add all third-party packages
// then open the folder in VSCode
  1. In the terminal of VSCode, run:
npm run dev

Then, a local host will begin.


File Intro under directory REACT-APP:

  1. node_modules folder:

All third-party libraries and tools are inside, no need to touch it.

  1. public folder:

Public assets of website, such as images and video files.

  1. source code.

index.html: basic template for html.

package.json: info of the project.

tsconfig.json: tell the compiler how to compile TypeScript to JS.


Under src file, create a file named: Message.tsx.

.ts and .tsx are both extenstions for TypeScript file.

(.ts for plain TS file, and .tsx for React component.)

Inside Message.tsx:

// need to use the format fo PascalCasing for naming
function Message(){
  // JSX: JavaScript XML, and will be converted to JS
  return <h1>Hello World</h1>;  
}

export default Message;

Inside App.tsx:

// import the module created above
import Message from './Message';

function App() {
  return <div><Message /></div>;
}

This is a simple example.

Can change Message.tsx:

// need to use the format fo PascalCasing for naming
function Message(){
  // JSX: JavaScript XML, and will be converted to JS

  // define a const, it acts as a dynamic var
  cont name = 'xxx'
  
  return <h1>{name}</h1>; 
  // inside {}, need sth to return a value
}

export default Message;

How React works.

Left side is the component tree we built. When our application starts, based on this tree React will build the Virtual Dom, which is a JS structure.

Virtual Dom, a light-weight in-memory representation of our tree, and one node matches one part in the tree. VD is different from the actual Dom in browser.

DOM stands for Document Object Model. It is a programming interface that allows us to create, change, or remove elements from the document. We can also add events to these elements to make our page more dynamic. The DOM views an HTML document as a tree of nodes.

When we change the component tree, VD will change with it and then compare to old version of VD to update the change to the actual Dom.

It is the Library named React Dom which does the job.

We can use another Library named React Native for mobile app.


Library: A tool that provides specific funtionality

Framework: A set of tools and guidelines for building apps


Install boostap, a CSS library:

npm i boostrap@5.2.3

In main.tsx file: replace import './index.css' with

import 'boostrap/dist/css/bootstrap.css'

2/27/2024

Need to continue...


index.css is the global style generated by vite, delete it here.

create a folder components and a file ListGroup.tsx inside.

copy the code for example list group from bootstrap website.

<ul class="list-group">
  <li class="list-group-item">An item</li>
  <li class="list-group-item">A second item</li>
  <li class="list-group-item">A third item</li>
  <li class="list-group-item">A fourth item</li>
  <li class="list-group-item">And a fifth one</li>
</ul>

then, add code into ListGroup.tsx

function ListGroup() {
    return <ul className="list-group">
    <li className="list-group-item">An item</li>
    <li className="list-group-item">A second item</li>
    <li className="list-group-item">A third item</li>
    <li className="list-group-item">A fourth item</li>
    <li className="list-group-item">And a fifth one</li>
  </ul>;
}

export default ListGroup;

Here, changed 'class' into 'className' due to jsx convention issue.

A tip here is to use ctrl+D to select all the same var name to make change.

Use ctrl+shift+p to open up a search for format document and choose prettier in configure (need to download prettier extention in vscode).

Another tip, use ctrl+p to search for file name in order to switch between files.

In App.tsx:

import ListGroup from "./components/ListGroup";

function App() {
  return <div><ListGroup /></div>;
}

export default App;

Then, the website should look like this:


A function is not allowed to return two elements at the same time.

If we want to do so, there are a few ways.

Method 1:

function ListGroup() {
  return (
    <div>
      <h1>List</h1>
      <ul className="list-group">
        <li className="list-group-item">An item</li>
        <li className="list-group-item">A second item</li>
        <li className="list-group-item">A third item</li>
        <li className="list-group-item">A fourth item</li>
        <li className="list-group-item">And a fifth one</li>
      </ul>
    </div>
  );
}

export default ListGroup;

Use a div to surround those two things we want to return.

Select them and use ctrl+shift+p to seach "wrap by abbreviation" and enter "div".

Method 2:

import { Fragment } from "react/jsx-runtime";

function ListGroup() {
  return (
    <Fragment>
      <h1>List</h1>
      <ul className="list-group">
        <li className="list-group-item">An item</li>
        <li className="list-group-item">A second item</li>
        <li className="list-group-item">A third item</li>
        <li className="list-group-item">A fourth item</li>
        <li className="list-group-item">And a fifth one</li>
      </ul>
    </Fragment>
  );
}

export default ListGroup;

Import Fragment from react and use it to surround what we want to return.

Method 3:


function ListGroup() {
  return (
    <>
      <h1>List</h1>
      <ul className="list-group">
        <li className="list-group-item">An item</li>
        <li className="list-group-item">A second item</li>
        <li className="list-group-item">A third item</li>
        <li className="list-group-item">A fourth item</li>
        <li className="list-group-item">And a fifth one</li>
      </ul>
    </>
  );
}

export default ListGroup;

Using <></> will tell react to use fragment.


jsx does not have loop, instead it uses map function.

const items = ["New York", "London", "Tokyo", "Boston", "Paris"];

items.map(item => <li>{item}</li>)  //map

Now the ListGroup.jsx file is like:

function ListGroup() {
  const items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  return (
    <>
      <h1>List</h1>
      <ul className="list-group">
        {items.map((item) => (        // need to use {}
          <li>{item}</li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

But now in the web page inspect, console has error on unique id.

function ListGroup() {
  const items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  return (
    <>
      <h1>List</h1>
      <ul className="list-group">
        {items.map((item) => (
          <li key={item}>{item}</li>       // add key here
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

If want to add logic, we can define a const var to hold the logic.

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  items = [];

 // use const var message to hold the logic
  const message = items.length === 0 ? <p>No item found</p> : null 

  return (
    <>
      <h1>List</h1>

      {message}

      <ul className="list-group">
        {items.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Or we can use a function.

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  items = [];

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  return (
    <>
      <h1>List</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Another technique.

{items.length === 0 ? <p>No item found</p> : null}

// is equivalent to 

{items.length === 0 && <p>No item found</p>}

We have a rule: true && k -> k ; false && k -> false

Do not forget to apply the css onto the list group items.

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  return (
    <>
      <h1>List</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item) => (
          <li className="list-group-item" key={item}>  // apply css
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Add onClick with id.

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  return (
    <>
      <h1>List</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item, index) => (   // added index
          <li
            className="list-group-item"
            key={item}
            onClick={() => console.log(item, index)}  // added line
          >
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

This is the result after clicking each item.

There is a build in group named "event" that contains item info.

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  return (
    <>
      <h1>List</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item, index) => (
          <li
            className="list-group-item"
            key={item}
            onClick={(event) => console.log(event)}  // changed code
          >
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Use a const to store the logic of click.

Here, we need to import MouseEvent from React and annotate event, since we need to let TS know the type of it.

import { MouseEvent } from "react";  // import

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  const handleClick = (event:MouseEvent) => console.log(event)  // new const

  return (
    <>
      <h1>List</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item, index) => (
          <li
            className="list-group-item"
            key={item}
            onClick={handleClick}
          >
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Use one hook useState to tell React we way change the state.

const arr = useState(-1);    // useState returns an array
arr[0]  // variable (selectedIndex)
arr[1]  // updater function

// conventional way to use
const [selectedIndex, setSelectedIndex] = useState(-1);  

Now our file will look like:

import { useState } from "react";   // added import

function ListGroup() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  const [selectedIndex, setSelectedIndex] = useState(-1);   // added state
  

  return (
    <>
      <h1>List</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item, index) => (
          <li
            className={ selectedIndex === index ? 'list-group-item active' : 'list-group-item'}
            key={item}
            onClick={() => {setSelectedIndex(index);}} // changed
          >
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Passing Props to make component reusable.

Define iterface, and pass in as argument. Pass in the args in App.tsx.

import { useState } from "react";

interface Props {    // define struct
  items: string[];
  heading: string;
}

function ListGroup(props: Props) {  // pass in

In App.tsx, pass in args.

import ListGroup from "./components/ListGroup";

function App() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];   // args

  return (
    <div>
      <ListGroup items={items} heading="Cities" />   // change along with interface
    </div>
  );
}

export default App;

In ListNode.tsx do the similar change.

import { useState } from "react";

interface Props {
  items: string[];
  heading: string;
}

function ListGroup({ items, heading }: Props) {   // decompose props
 

  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  const [selectedIndex, setSelectedIndex] = useState(-1);
  

  return (
    <>
      <h1>{heading}</h1>    // use heading

      {getMessage()}

      <ul className="list-group">
        {items.map((item, index) => (
          <li
            className={ selectedIndex === index ? 'list-group-item active' : 'list-group-item'}
            key={item}
            onClick={() => {setSelectedIndex(index);}}
          >
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

Add another property to tell the App what list is clicked.

import { useState } from "react";

interface Props {
  items: string[];
  heading: string;
  onSelectItem: (item: string) => void;   // added 
}

function ListGroup({ items, heading, onSelectItem }: Props) {    // added 
  const getMessage = () => {
    return items.length === 0 ? <p>No item found</p> : null;
  };

  const [selectedIndex, setSelectedIndex] = useState(-1);

  return (
    <>
      <h1>{heading}</h1>

      {getMessage()}

      <ul className="list-group">
        {items.map((item, index) => (
          <li
            className={
              selectedIndex === index
                ? "list-group-item active"
                : "list-group-item"
            }
            key={item}
            onClick={() => {
              setSelectedIndex(index);
              onSelectItem(item);       // added 
            }}
          >
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default ListGroup;

import ListGroup from "./components/ListGroup";

function App() {
  let items = ["New York", "London", "Tokyo", "Boston", "Paris"];

  const handleSelectItem = (item: string) => {     // added handler
    console.log(item);
  };

  return (
    <div>
      <ListGroup
        items={items}
        heading="Cities"
        onSelectItem={handleSelectItem}     // added
      />
    </div>
  );
}

export default App;

Alert

Create a file Alert.tsx under file component.

Download extension ES7+ and type rafce to get a template:

import React from 'react'

const Alert = () => {
  return (
    <div>Alert</div>
  )
}

export default Alert

Delete the content in App.tsx and change it to match with Alert.

import Alert from "./components/Alert";


function App() {
  

  return (
    <div> 
      <Alert>
        Hello  <span>World</span>     // since we pass html content
      </Alert>                        // we need to use ReactNode in Alert.tsx
    </div>
  );
}

export default App;

import { ReactNode } from 'react'          //import

interface Props {
    children: ReactNode;               // use children, and ReactNode(mixed struct)
}

const Alert = ({ children }: Props) => {
  return (
    <div className='alert alert-primary'>{children}</div> // className for alert
  )                                                // borrowed from bootstrap                                                                               
}

export default Alert

Button

Create a file Button.tsx under folder component.

import React from 'react'

interface Props {
  children: string  
  onClick: () => void;
  
  color?: string; 
  // ? is saying that the property is optional
  // can set to specific color to make limitation: 'primary'|'danger'|'secondary';
}

const Button = ({ children, onClick, color = 'primary'}: Props) => {
  return (
    <button className={'btn btn-' + color} onClick={onClick}>{ children }</button>
  )
}

export default Button

import Button from "./components/Button";


function App() {
  

  return (
    <div> 
      <Button color='secondary' onClick={() => console.log('clicked')}>My Button </Button>
    </div>
  );
}

export default App;

Alert Button + close Button

Button.tsx no change

Alert.tsx

import { ReactNode } from "react";

interface Props {
  children: ReactNode;
  onClose:() => void;
}

const Alert = ({ children, onClose }: Props) => {
  return (
    <div className="alert alert-primary alert-dismissible">
        {children}
        <button type="button" className="btn-close" onClick={onClose} data-bs-dismiss="ale"></button>
    </div>
    
  );
};

export default Alert;

App.tsx

import Button from "./components/Button";
import Alert from "./components/Alert";
import { useState } from "react";


function App() {
  const [alertVisible, setVisibility] = useState(false);

  return (
    <div> 
      { alertVisible && <Alert onClose={() => setVisibility(false)}>My alert</Alert>}
      <Button color='secondary' onClick={() => setVisibility(true)}>My Button </Button>
    </div>
  );
}

export default App;

Link from 'react-router-dom' :

  • The Link component is used to create links that navigate to different routes in your application without causing a full page reload. It's the basic building block for navigation within a React Router application.
  • When you use a Link, it simply changes the URL and tells React Router to update the view accordingly, without additional behavior or styling based on the navigation state.

NavLink from 'react-router-dom' :

  • NavLink is a special kind of Link that can style itself as "active" when it matches the current location. It's used when you want to provide visual feedback to the user about which route is currently active, like highlighting the current page in a navigation bar.
  • NavLink has an isActive prop that can be used to add extra logic for determining whether the link is active, and it automatically receives an "active" class (or any class you specify) when the link's destination matches the current pathname.

Example of creating a Navbar.tsx component:

// navigate throught different pages.
// buttons are on the top

import React from 'react';
import { NavLink } from 'react-router-dom';


function Navigation() {
  return (
    <div className="topnav">
      <NavLink to="/" className={({ isActive }) => isActive ? "active" : ""}>
        Home
      </NavLink>
      <NavLink to="/fileconvert" className={({ isActive }) => isActive ? "active" : ""}>
        File Convert
      </NavLink>
      <NavLink to="/translator" className={({ isActive }) => isActive ? "active" : ""}>
        File Translator
      </NavLink>
      <NavLink to="/user" className={({ isActive }) => isActive ? "active" : ""}>
        User
      </NavLink>
    </div>
  );
}

export default Navigation;

Corresponding App.tsx using react-router:

import "../src/styles/main.scss";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Dashboard from "./pages/Dashboard";
import Fileconvert from "./pages/Fileconvert";
import Translator from "./pages/Translator";
import User from "./pages/User";
import Navigation from "./components/Navigation";
//import Sidebar from "./components/Sidebar";

function App() {
    return (
        <Router>
            <div className="App">
                <Navigation />
                <Routes>
                    <Route path="/" element={<Dashboard />} />
                    
                    <Route path="/fileconvert" element={<Fileconvert />} />
                    
                    <Route path="/translator" element={<Translator />} />

                    <Route path="/user" element={<User />} />
                </Routes>
            </div>
        </Router>
    );
}

export default App;

  • .css: Stands for Cascading Style Sheets. It's a stylesheet language used for describing the presentation of a document written in HTML or XML. CSS is the basic and traditional way of styling web pages.
  • .scss: SCSS (Sassy CSS) is a syntax for Sass (Syntactically Awesome Style Sheets), which is a CSS preprocessor. SCSS provides more advanced features than standard CSS, such as variables, nesting, mixins, inheritance, and other handy tools.
  • .scss can use variable to replace colour
$primary-color: blue;

.button {
  color: $primary-color;
  background-color: white;
  &:hover {
    color: white;
    background-color: $primary-color;
  }
   
// can also import other file with defined var using

@import "./_variables";

We can create a main.scss file to customize style component by component.

body {

}

h1 {

}

.navbar {

}

......