These learning notes are derived from “Learn React in 90 minutes” video series by Miguel Grinberg.

Part 1: Rendering

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

  • React will generate an HTML file at final process
  • A component is a function (a new way - functional components) - return what you want to see on the rendered page. The main component is usually app.js
  • Example:
export default function App() {
  const username = "Hoa Nguyen";
  return (
    <div className="App">
      <h1>Hi! {username}</h1>
      <h2>Part 1: Rendering</h2>
    </div>
  );
}
  • We can mix HTML and JS together but we should separate and don’t mix them too much (separate the JS logic and HTML represent)
    – This is not really Javascript, we render some of regular Javascript from it (jsx)
  • Basics: If - Else clause and for loop
  • Example
export default function App() {
  const username = "Hoa";
  const todos = ["Task 1", "Task 2", "Task 3"];
    
  return (
    <div className="App">
      {/* If = Else (:) */}
      {username ? <h2> Hi {username}! </h2> : <h2> Hi Stranger! </h2>}
      <h2>Part 1: Rendering</h2>
    
      {/* If only */}
      {username && <a href="#logout">Logout</a>}
      <br />
    
      {/* For loop */}
      <ul>
        {todos.map((todo) => (
          <li>{todo}</li>
        ))}
      </ul>
    </div>
  );
}

Part 2: The State Hook

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

  • If we use a normal variable, every time the component is rendered, it will be the default value that you set
    const counter = 0;useState function from React
  • useState is one of the most important things in React, anytime we want to display information on a page that need to update
  • useState will trigger all the updates made within the React app → refresh all the references within the page
  • Example
// useState return Variable and a setter function (how React detect change)
const [counter, setCounter] = React.useState(0);
  const increase = () => {
    setCounter(counter + 1);
  };
  const decrease = () => {
    setCounter(counter - 1);
  };
...
return (
    <div className="App">
			<h2>Part 2: The State Hook </h2>
      <p>Counter: {counter}</p>
      <div>
        <button onClick={increase}>+1</button>
        <button onClick={decrease}>-1</button>
      </div>
		</div>
	  );
		}

Part 3: Sub-Components

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

Main goal: Reusable component.

  • App.js file
import React from "react";
import "./styles.css";
import Child from "./Child.js";
    
export default function App() {
  // Part 3
  // useState return Variable and a setter function (how React detect change)
  const [counter, setCounter] = React.useState(1);
    
  return (
    <div className="App">
      <h2>Part 3: The Sub-Components </h2>
      <p>Counter: {counter}</p>
      {/* Insert child component (props) */}
      <Child step={1} counter={counter} setCounter={setCounter} />
      <br />
      <Child step={5} counter={counter} setCounter={setCounter} />
    </div>
  );
}
  • Child.js
import React from "react";
    
// Old way: export default function Child(props)  => Use: props.counter, prop.setCounter
// Modern way: use bracket {counter, setCounter}
export default function Child({ step, counter, setCounter }) {
  const increase = () => {
    setCounter(counter + step);
  };
  const decrease = () => {
    setCounter(counter - step);
  };
    
  return (
    <div>
      <button onClick={increase}>+{step}</button>
      <button onClick={decrease}>-{step}</button>
    </div>
  );
}import React from "react";
import "./styles.css";
import Child from "./Child.js";
    
export default function App() {
  // Part 3
  // useState return Variable and a setter function (how React detect change)
  const [counter, setCounter] = React.useState(1);
    
  return (
    <div className="App">
      <h2>Part 3: The Sub-Components </h2>
      <p>Counter: {counter}</p>
      {/* Insert child component (props) */}
      <Child step={1} counter={counter} setCounter={setCounter} />
      <br />
      <Child step={5} counter={counter} setCounter={setCounter} />
    </div>
  );
}
  • Minimise the component
    App.js
<Child step={1} setCounter={setCounter} />
<br />
<Child step={5} setCounter={setCounter} />
  • Child.js
const increase = () => {
    setCounter((x) => x + step);
  };
  const decrease = () => {
    setCounter((x) => x - step);
  };

Part 4: The Effect Hook

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

  • When using useEffect(), you will set a second response with an empty list []
React.useEffect(() => {
    
  }, []);
  • It uses to define what to trigger when its value changes
React.useEffect(() => {
    fetch("https://freecurrencyapi.net/api/v2/latest?apikey=xxxx&base_currency="+currency)
      .then((res) => res.json())
      .then((data) => {
        // console.log(data);
        setRates(data.data);
      });
  }, [currency]);
  • Example of useEffect()
import React from "react";
import "./styles.css";
    
export default function App() {
  // Part 4
  // const rates = {
  //   GDP: 1.22,
  //   EUR: 0.9
  // };
  const [rates, setRates] = React.useState({});
  const [currency, setCurrency] = React.useState('USD');
    
  // React Effect function
  React.useEffect(() => {
    fetch("https://freecurrencyapi.net/api/v2/latest?apikey=xxx&base_currency="+currency)
      .then((res) => res.json())
      .then((data) => {
        // console.log(data);
        setRates(data.data);
      });
  }, [currency]);
    
  const setUSD = () => setCurrency('USD');
  const setEUR = () => setCurrency('EUR')
    
  return (
    <div className="App">
      <h2>Part 4: The Effect Hook</h2>
      <button onClick={setUSD}>USD</button>  <button onClick={setEUR}>EUR</button>
      <h3>{currency} Exchange Rate</h3>
      {Object.keys(rates).map((currency) => (
        <li>
          {currency}: {rates[currency]}
        </li>
      ))}
    </div>
  );
}

Part 5: The Ref Hook

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

  • Main goal: To interact with some libraries that are not built with React (non-React component)
import React from "react";
import "./styles.css";
    
export default function App() {
  const text = React.useRef();
  const onFocus = () => { text.current.style.background = "#ddf";}
  const onBlur = () => {text.current.style.background = "#fff";}
    
  React.useEffect(() => {
    // Trick: Run a function when component is removed
    const myText = text.current;
    // use .current to get current Ref
    console.log(text.current);
    // text.current.focus();
    // text.current.style.background = "#ddd";
    myText.addEventListener('focus', onFocus);
    myText.addEventListener('blur', onBlur);
    
    return () => {
      // Cleanup the component (Remove all EventListener) when it is being removed
      myText.removeEventListener('focus', onFocus);
      myText.removeEventListener('blur', onBlur);
    }
  }, []);
  return (
    <div className="App">
      <h1>Part 5: The Ref Hook</h1>
      {/* Use plain JS input */}
      <input type="text" ref={text} />
    </div>
  );
}
  • Cleanup the component (Remove all EventListener) when it is being removed
return () => {
      // Cleanup the component (Remove all EventListener) when it is being removed
      myText.removeEventListener('focus', onFocus);
      myText.removeEventListener('blur', onBlur);
    }
  • Trick: Use a temporary variable
    const myText = text.current;

Part 6: The Context Hook

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

  • Scenario: We need to share the username between the LoginForm and the Header
    – If using State variable with location high enough for all sub-component can access
const [username, setUsername] = React.useState(null);
  • When we need to share (pass) information between multiple components
    → Using Context to create a shared state that is accessible to a group of components without having to explicitly pass those elements of the states as props
  • Make the context accessible to all components by adding a wrapper <UserContext.Provider>
const currentUser = {
    username: 'hoant',
  };
...
return (
...
<UserContext.Provider value={currentUser}>
  <Navigation />
  <Header />
</UserContext.Provider>
);
  • App.js
import React from "react";
import "./styles.css";
import Navigation from "./Navigation.js";
import Header from "./Header.js";
    
// Make it exportable (public) access to other components
export const UserContext = React.createContext();
    
export default function App() {
  // A messy way: Use State var and pass to all sub-components
  const [username, setUsername] = React.useState(null);
  // Share the currentUser to the Context
  const currentUser = {
    username: username,
    // Add a function to log the user in (perform login procedure)
    // - can be use from the login form to do the login
    // Receive the username (_username) and set the state to logged in user
    loginUser: (_username) => {
      setUsername(_username);
    },
    // Log out
    logoutUser: () => {
      setUsername(null);
    }
  };
  return (
    <div className="App">
      <h1>Part 6: The Context Hook</h1>
      {/*  Add Wrapper to make context accessible to all children */}
      {/* Give currentUser value to the context */}
      <UserContext.Provider value={currentUser}>
        <Navigation />
        <Header />
      </UserContext.Provider>
    </div>
  );
}
  • Create Context and make it accessible to all sub-components by export
export const UserContext = React.createContext();
  • Add loginUser and logoutUser function to currentUser
  const currentUser = {
    username: username,
    // Add a function to log the user in (perform login procedure)
    // - can be use from the login form to do the login
    // Receive the username (_username) and set the state to logged in user
    loginUser: (_username) => {
      setUsername(_username);
    },
    // Log out
    logoutUser: () => {
      setUsername(null);
    }
  };
  • Header.js
import React from "react";
// Import context from App.js
import { UserContext } from "./App.js";
    
export default function Header() {
  const currentUser = React.useContext(UserContext);
  console.log(currentUser);
    
  return (
    <div>
      {/* If conditional to check if username is set */}
      {currentUser.username ? (
        <p> Welcome, {currentUser.username}!</p>
      ) : (
        <p> Please login</p>
      )}
    </div>
  );
}
  • Navigation.js
import React from "react";
import LoginForm from "./LoginForm.js";
    
export default function Navigation() {
  return (
    <div>
      <LoginForm />
    </div>
  );
}
  • LoginForm.js
import React from "react";
import { UserContext } from "./App.js";
    
export default function LoginForm() {
  // Using Ref to extract the username whatever we type in the form when it is submitted
  const username = React.useRef();
  const currentUser = React.useContext(UserContext);
    
  const onSubmit = (ev) => {
    //  ev = EvenHandler
    // Prevent the browser submit the form as network request or HTTP request
    // We handling the form in client side -> Prevent browser submission happening
    ev.preventDefault();
    console.log(username.current.value);
    // Pass the current value of username from
    currentUser.loginUser(username.current.value);
  };
    
  const logOut = () => {
    currentUser.logoutUser();
  };
    
  return (
    <div>
      {currentUser.username == null ? (
        <form onSubmit={onSubmit}>
          <input type="text" ref={username} />
          <input type="submit" value="Login" />
        </form>
      ) : (
        <button onClick={logOut}>Logout</button>
      )}
    </div>
  );
}

Part 7: Memoization

(Advanced topic)

Sample Code: https://codesandbox.io/s/reactjs-part-12-stbdh0

A way to optimise the application to run faster by doing fewer renders (make sure that component is only rendered when they really need to be)

→ Prevent the component’s rendering if its internal states do not change

E.g. Prevent reset (in Child2.js) to be rendered when not necessary

  • Child2.js
import React from "react";
    
// Pass setCounter as a prop
export default function Child2({ setCounter }) {
  console.log("Child2");
    
  const reset = () => {
    setCounter(0);
  };
  return <button onClick={reset}>Reset</button>;
}

It will be rendered every time the button is clicked

→ Prevent rendering child2 by using memo funtion

  • Ideas: Tell react remember the first version of the component when it is created and keep reusing it instead of generating new versions of the component that need to be re-rendered
    → Wrap the Child2 function in the memo
const Child2 = ({ setCounter }) => {
  console.log("Child2");
        
  const reset = () => {
    setCounter(0);
  };
  return <button onClick={reset}>Reset</button>;
}
        
export default Child2;

⇒ Wrap it

const Child2 = React.memo(({ setCounter }) => {
  console.log("Child2");
        
  const reset = () => {
    setCounter(0);
  };
  return <button onClick={reset}>Reset</button>;
});
        
export default Child2;

Result: The Child2 is rendered once and never again

The Component will be re-rendered if any of its props or state change

  • Improve: Keep the state in the component that owns the state
<Child2 setCounter={setCounter} />
    
// Change to
const reset = () => {
    setCounter(0);
  };
<Child2 reset={reset} />

On Child2:

const Child2 = React.memo(({ setCounter }) => {
  console.log("Child2");
    
  const reset = () => {
    setCounter(0);
  };
  return <button onClick={reset}>Reset</button>;
});
    
// Change to
const Child2 = React.memo(({ reset }) => {
  console.log("Child2");
    
  return <button onClick={reset}>Reset</button>;
});

However, we were back at rendering the child component every time

  • Why?
    Each time the parent component re-renders, it will create a new reset ⇒ the internal state change ⇒ the Child2.js will re-render
const reset = () => { setCounter(0);};

⇒ Use useCallback() - React will be remember the reset and always bringing back to the original function

const reset = React.useCallback(() =>{setCounter(0);}, []);