Learn ReactJS Fundamentals: 7 core features

Hoa Nguyen (hoaio)
8 min readMar 29, 2024

--

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);}, []);

--

--