REACT THE COMPLETE GUIDE 2024
What is React?
React Is a Library for Building User Interfaces
It’s an JavaScript Library
React Builds up on Javascript (Its an Library)
React is helpful to build the Complex user interface and web application
React is used to build an Single-Page Application
React allows us to create reusable UI components
Commands :
Used to create the project in React us the command : cd project-name (EX : cd my-react-app)
And start the react app use command : npm start
Default the Browser run’s the react project on : localhost:3000
ES6 IN REACT
ES6 CLASS FEATURE:
A class is the type of the function , instead of using the function keyword we can use the keyword class and the properties are assigned inside the constructor() method
Ex :
Class Car{
Constructor(name){
this.name=name;
}
}
//Now we can create the Object for the class
const mycar=new Car("BMW");
Method’s In Class :
EX:
Class Car{
constructor(name){
this.name=name;
}
Present(){
return 'I have a '+ this.name;
}
}
Const mycar =new Car("BMW");
Mycar.Present();
CLASS INHERITANCE :
To create the class Inheritance we need to use the extends keyword
Ex :
Class Car{
constructor(name){
this.name=name;
}
Present(){
return 'I have a '+ this.name;
}
}
Class Model extends Car{
constructor(name,mod){
Super.name();
this.mod=mod;
}
Show(){
return this.present()+' , its an '+ this.mod;
}
}
const mycar=new Model();
Mycar.show();
In this, super() method its refers to the parent class By calling the super() method in the constructor we call the parent’s constructor method and get access to the parent’s properties and methods
ES6 ARROW FUNCTION :
Its used to write the shorter function syntax
Ex :
//Its an normal function
Hello = function(){
return "Hello world";
}
//Its an Arrow Function
Hello=()=>{
return "Hello world";
}
//We can write the arrow function like this as well
Hello=()=>"Hello world";
//If you want to pass an parameter we can like below
Hello=(val)=>"Hello"+val;
ES6 VARIABLES :
We can define the variables using var keyword , if you don’t mentioned that it will be assigned as a global object
We can define your variables var,let,const
Ex : var x=10;
If you use varoutside of a function , its belongs to the Global Scope
If you use var inside of a function, its belongs to that function
If you use var inside of a Block , i.e a for loop , the variable still available outside of that block
Ex : let x=20;
Let is an block scope variable , if you use let inside the block it will be accessible for that block only
Ex : const x=30;
Once if you declared as const variable you can’t able to change the value of the variable
ES6 ARRAY METHODS :
There are many JS array methods
One of the most useful React is the .map() array method
The .map() used to generate the Lists
Ex : const myarray[]=['apple','orange','mango'];
const mylist=myarray.map((item)=><p>{item}</p>)
ES6 DESTRUCTURING :
We may have an array or object that we are working with , but we only we need some of items contained in these
// these are general way
Ex : const vehicles =['BMW','AUDI','BENZ'];
//Destructing way
Ex : const vehicles=['BMW','JCB','SUMO'];
const[car,truck,suv]=vehicles;
ES6 SPREAD OPERATOR :
The JS Spread Operator(…) allows us to quickly copy all or part of an existing array or object into another array or object
EX :
//Spread Operator is often used in combination with destructuring
const arr1=[1,2,3];
const arr2=[4,5,6];
const numbers=[…arr1,…arr2];
Ex :
const numbers=[1,2,3,4,5,6,7,8];
const[one,two,…rest]=numbers;
We can use the Spread Operator with Objects too:
const myvehicle={
Brand : 'Foard',
Model : 'Mustang',
Color : 'Red'
}
const updatedMyVehicle={
Type : 'car',
Year :2021,
Color : 'yellow'
}
const myupdateDetails={…myvehicle,…updatedMyVehicle}
ES6 TERNARY OPERATOR :
The ternary operator is a simplified conditional operator like if/else
Syntax : condition ? <expression if true> : <expression if false>
Ex : authenticated ? renderApp() : renderLogin();
React Render HTML
React goals is in many ways to render HTML in a web page
React renders the HTML web page using a function called createRoot() and render()
CreateRoot() : it takes one argument an HTML element , The Purpose of the function is to define the HTML element where a React Components should be displayed
Render() : its used to define the React Component that should be rendered
React JSX
JSX – JavaScript XML
JSX allows us to write HTML in React
JSX makes it easier to write and add HTML in React
JSX converts HTML elements into React Elements
You can write the Expressions inside the {} braces
React Components
Components are like functions that return HTML elements
Components are independent and reusable bits of code
Components have two Types CLASS , FUNCTION components
Class Component :
Class Component must include the “extends React.Component” statement , This statement creates an Inheritance to React.Component , and gives your Component access to React.Component’s functions
The component also requires a render() method this method returns HTML
EX :
class car extends React.Component {
render(){
return <h2>Hi , I am a Car!</h2>
}
}
Function Component :
Its also will returns HTML and behaves much the same as Class Component , but function component can be written in less lines of code and easier to understand
EX :
function Car(){
return <h2> Hi , I am a Car!</h2>;
}
const root=ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car/>);
Props(Properties)
Components can be passed as Props which stands for Properties
Props are like function arguments and you send them into the components as attributes
EX :
function Car(props){
return <h2> I am a {props.color} Car!</h2>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car color="red"/>);
React Events
React have same HTML DOM events like ,click , change , mouseover etc..
React Events are written in CamelCase Syntax onClick instead of onclick
React Events handlers are written inside the curly braces{}
EX : onClick={shoot} instead of onclick="shoot()"
EX : REACT
<button onClick={shoot}>Take a Shot</button>
EX : HTML
<button onclick=”shoot()”>Take the Shot!</button>
React Conditional Rendering
In React you can Conditionally render Components
There are several ways to do this
If statement : when we use if operator Javascript operator will decide which component to be render
Logical && Operator : && operator to check the condition if the condition is true it will be render the component
Ternary Operator : condition ? true : false;
React Lists :
We can render the list in any type of the loop
Map() method is generally preferable
EX :
function Car(props) {
return <li>I am a { props.brand }</li>;
}
function Garage() {
const cars = ['Ford', 'BMW', 'Audi'];
return (
<>
<h1>Who lives in my garage?</h1>
<ul>
{cars.map((car) => <Car brand={car} />)}
</ul>
</>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Garage />);
REACT FORMS :
React uses forms to allow users to interact with the web page
EX :
function MyForm() {
return (
<form>
<label>Enter your name:
<input type="text" />
</label>
</form>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyForm />);
Handling Forms :
Handling forms is about how you handle the data when it changes value or get submitted
In HTML form data usually handled by the DOM
In React form data usually handled by React Components
When the data is handled by the components all the data is stored in the component state
We can control the changes by adding the event handler in the “onChange” attribute
We can use the useState Hook to keep track of each inputs values and provide “single source of truth” for the entire application
EX :
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
function MyForm() {
const [name, setName] = useState("");
return (
<form>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
</form>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyForm />);
SUBMITTING FORMS :
We can control the submit action by adding an event handler in the “onSubmit” attribute for the “<form>”
EX :
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
function MyForm() {
const [name, setName] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
alert(`The name you entered was: ${name}`)
}
return (
<form onSubmit={handleSubmit}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<input type="submit" />
</form>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyForm />);
MULTIPLE INPUT FIELDS :
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
function MyForm() {
const [inputs, setInputs] = useState({});
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
setInputs(values => ({...values, [name]: value}))
}
const handleSubmit = (event) => {
event.preventDefault();
alert(inputs);
}
return (
<form onSubmit={handleSubmit}>
<label>Enter your name:
<input
type="text"
name="username"
value={inputs.username || ""}
onChange={handleChange}
/>
</label>
<label>Enter your age:
<input
type="number"
name="age"
value={inputs.age || ""}
onChange={handleChange}
/>
</label>
<input type="submit" />
</form>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyForm />);
TEXT-AREA :
Textarea element is slightly different from ordinary HTML
In HTML generally textarea tag it will be like an
EX :
<textarea> Hello </textarea>
In React we can use the useState Hook to manage the value of the Textarea
EX :
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
function MyForm() {
const [textarea, setTextarea] = useState(
"The content of a textarea goes in the value attribute"
);
const handleChange = (event) => {
setTextarea(event.target.value)
}
return (
<form>
<textarea value={textarea} onChange={handleChange} />
</form>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyForm />);
SELECT :
A drop down list or a select box , in React is also a bit different from HTML
In HTML the selected value in the drop down list was defined with the selected attribute
EX :
<select>
<option value="Ford">Ford</option>
<option value="Volvo" selected>Volvo</option>
<option value="Fiat">Fiat</option>
</select>
EX :
function MyForm() {
const [myCar, setMyCar] = useState("Volvo");
const handleChange = (event) => {
setMyCar(event.target.value)
}
return (
<form>
<select value={myCar} onChange={handleChange}>
<option value="Ford">Ford</option>
<option value="Volvo">Volvo</option>
<option value="Fiat">Fiat</option>
</select>
</form>
)
}
REACT STYLING USING CSS
There are many ways to style React with CSS
Inline styling , CSS stylesheets , CSS modules
Inline Styling :
const Header = () => {
return (
<>
<h1 style={{color: "red"}}>Hello Style!</h1>
<p>Add a little style!</p>
</>
);
}
const Header = () => {
return (
<>
<h1 style={{color: "red"}}>Hello Style!</h1>
<p>Add a little style!</p>
</>
);
}
CSS Stylesheet :
body {
background-color: #282c34;
color: white;
padding: 40px;
font-family: Sans-Serif;
text-align: center;
}
CSS MODULES :
CSS modules are very convenient for the components that are placed in separate files
EX :
.bigblue {
color: DodgerBlue;
padding: 40px;
font-family: Sans-Serif;
text-align: center;
}
REACT HOOKS :
Hooks were added in React in version 16.8
Hooks allow function components to have access to state and other React Features
Hooks are generally replaces the class components
Hooks have an features such as state and life cycle
Hook Rules :
Hooks will be called inside the React Components
Hooks can only be called at the top level of a component
Hooks cannot be conditional
Hooks will not work on Class Components
React useState Hook :
Its used to allows us to track state in a function component
State generally refers to data or properties that need to be tracking in an application
Initialize useState :
We initialize our state by calling useState in our function Component
useState accepts an initial state and returns two values
The Current state
A function that Updates The state
EX :
//initialize the state at the top of the function component
import { useState } from "react";
function FavoriteColor() {
const [color, setColor] = useState("");
}
Notice that again, we are destructuring the returned values from useState.
The first value, color, is our current state.
The second value, setColor, is the function that is used to update our state.
These names are variables that can be named anything you would like.
Lastly, we set the initial state to an empty string: useState("")
Read State :
EX :
import { useState } from "react";
import ReactDOM from "react-dom/client";
function FavoriteColor() {
const [color, setColor] = useState("red");
return <h1>My favorite color is {color}!</h1>
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<FavoriteColor />);
UPDATE STATE :
EX :
import { useState } from "react";
import ReactDOM from "react-dom/client";
function FavoriteColor() {
const [color, setColor] = useState("red");
return (
<>
<h1>My favorite color is {color}!</h1>
<button
type="button"
onClick={() => setColor("blue")}
>Blue</button>
</>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<FavoriteColor />);
React useEffect Hooks :
The useEffect Hook allows you to perform side effects in your components
The useEffect accepts two arguments The second argument is optional
useEffect(<function>,<dependency>)
EX :
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
});
return <h1>I've rendered {count} times!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
In this example useEffect runs on every render That means that when the count changes , a render happens which the triggers another effect
This is not what we want , we should always include the second parameter which accepts an array we can optionally pass dependencies to useEffect in this array
EX :
No dependency passed :
useEffect(()=>{
//Runs on every render
});
EX :
//An empty array
useEffect(()=>{
//Runs only on the first render
},[]);
EX :
Props or state values
useEffect(()=>{
//Runs on the first render
//and any time any dependency value changes
},[prop,state]);
React useRef Hook :
Its used to persist values between renders
It can be used to store a mutable value that does not cause a re-render when updated
It can be used to access a DOM directly
We can use combination of useState , useEffect , and useRef to keep track of the previous state
React useReducer Hook :
useReducer Hook is similar to the useState Hook
It allows for Custom State Logic
The useReducer Hook accepts 2 arguments
useReducer(<reducer>,<initialState>)
React useCallback Hook :
Its used to returns a memoized callback function
Its caching the value so that it does not need to be re-calculated
It will not automatically run on every render
useCallback Hook only runs when one of its dependencies update
this can improve the performance
useCallback and useMemo Hooks are similar , The main difference is that useMemo return a memoized value and useCallback returns a memorized function
COMPONENTS :
Creating and nesting Components :
React app are made out of components , A component is an piece of the UI that has its own logic and appearance. A component can be as small as a button or as large as an entire page
EX :
//React components are JS functions that return markup
function MyButton(){
return (
<button>I’m a Button</button>
);
}
Now we can Nest an MyButton into another Component
EX :
Export default function MyApp(){
return(
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
Notice Above <MyButton/> starts with a capital letter , That’s how you know It’s an React Component , React Component names muts be always starts with a capital letter , while HTML tags must be lowercase
The export default keywords specify the main component in the file
WRITTING MARKUP WITH JSX
JSX is called markup syntax It’s an optional, but most React Projects use JSX for its convenience
JSX tags you can wrap them into shared parenr , like a <div>…</div> or an empty <>..</> wrapper
EX :
function AboutPage(){
return(
<>
<h1>About</h1>
<p>Hello Hi</p>
</>
);
}
ADDING STYLES:
In React you specify a CSS class with className . it works the same way as the HTML class attribute
<img className="avatar"/>
CSS FILE :
.avatar{
border-radius:50%;
}
DISPLAYING DATA :
EX :
return(
<h1>
{user.name}
</h1>
);
CONDITIONAL RENDERING :
EX:
Let content;
if(isLoggedIn){
content=<AdminPanel/>;
}
else{
content=<LogInForm/>;
}
return(
<div>
{content}
</div>
);
We can write the above code more compact , use conditional ? operator
EX:
<div>
{isLoggedIn ? (
<AdminPanel/>
) : (
<LoginForm/>
)}
</div>
RENDERING LISTS :
For rendering lists we can use the JS features like for loop and the array map() function to render lists of components
EX:
const products=[
{title : ‘A’ , id:1},
{title:’B’,id:2},
{title:’C’,id:3},
];
Inside component use the map(0 function to transform an array of products into an array of <li> items
EX :
const listItems = products.map(product=>
<li key={product.id}>
{product.title}
</li>
);
return(
<ul>{listItems}</ul>
);
RESPONDING TO EVENTS :
We can respond to the events by handling the event handler function inside your components
EX :
function MyButton(){
function handleClick(){
alert('You Clicked Me');
}
return(
<button onClick={handleClick}>Click Me</button>
);
}
UPDATING THE SCREEN :
Is that you’ll want your component to “remember” some information and display it
EX : maybe you want to count the number of times button is clicked , to do this add state to your component
First , import useState from React
import {useState} from ‘react’;
Now you can declare the state variable inside the component
function myfunction(){
Const[count,setCount]=useState(0);
}
You’ll get two things from useState : the current state (count) and the function that lets you update it (setCount) , you can give them any names , but the convention is to write [something , setSomething]
The first time the button is displayed count will be 0 because you passed 0 to useState() when you want to change state call, setCount() and pass the new value to it . Clicking this button will increment the counter
EX :
function MyButton(){
const[count,setCount]=useState(0);
function handleClick(){
setCount(count+1);
}
return(
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
USING HOOKS :
Function starting with use are called Hooks , useState is a built-in Hook provided by React , you can find other built-in Hooks in the API reference
We can write the own Hooks by combining the existing ones
Hooks can be call only at top of the Components or Top of other Hooks
DESCRIBING THE UI :
React is a JS library for rendering UI
UI is built from small units like buttons,text,and images
React lets you combine them into reusable,nestable components
YOUR FIRST COMPONENT :
Components are one of the core concepts of React , They are the foundation upon which you build UI
STEP 1 : EXPORT THE COMPONENT
The export default prefix is a Standard JS Syntax , its lets you mark the main function in a file so that you can later import it from other files
STEP 2 : DEFINE THE FUNCTION
With function Profile() {} you define a JS function with the name Profile
STEP 3: ADD MARKUP
The component returns an <img/> tag with src and alt attributes <img/> is written like HTML , but its actually JS under the hood! This syntax is called JSX
return <img src=https://i.img.com/MKHFg.jpeg alt=”Kathy”/>;
USING COMPONENT :
Now you defined Profile component , you can nest it inside other components
EX : you can export Gallery component that uses multiple Profile Components
function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3As.jpg"
alt="Katherine Johnson"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
WHAT THE BROWSER SEES :
<section> is lowercase , so React knows we refer to an HTML tag
<Profile/> starts with capital P , so React knows that we want to use our components called Profile
IMPORTING AND EXPORTING COMPONENTS :
The ROOT Component :
EX : In your first Component you made a Profile component and a Gallery component that renders it
function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3As.jpg"
alt="Katherine Johnson"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
EXPORTING AND IMPORTING COMPONENT
We can move Components in 3 steps
1.Make a new JS file to put the Component in
2.Export your function component
3.import the file where you will use the component
EX :
APP.JS
import Gallery from './Gallery.js';
export default function App() {
return (
<Gallery />
);
}
Gallery.js
function Profile() {
return (
<img
src="https://i.imgur.com/QIrZWGIs.jpg"
alt="Alan L. Hart"
/>
);
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
WRITING MARKUP WITH JSX
JSX is an Syntax extension for JS that lets you write HTML like Markup inside a JS file
**JSX:**PUTTING MARKUP INTO JS
The Web has been built on HTML,CSS,and JS
The web Became more interactive , logic increasing determined content , This is why React rendering logic and markup live together in the same place components
EX:
Form(){
onClick(){…}
onSubmit(){…}
<form onSubmit>
<input onClick/>
<input onClick/>
</form>
}
THE RULES OF JSX
1.RETURN A SINGLE ROOT ELEMENT :
To return multiple elements from component wrap them with a single parent tag
EX :
You can use <div> tag
<div>
<h1>…</h1>
<img src=”….” Alt=”..” class=”…”>
<ul>….</ul>
</div>
If you don’t want to add an extra <div> to your markup , you can write instead of <> and </>
EX :
<>
<h1>…</h1>
<img src=”….” Alt=”..” class=”…”>
<ul>….</ul>
</>
This empty tag is called a “Fragment” – Fragments let you group things without leaving any trace in the browser HTML tree
2.CLOSE ALL THE TAGS
JSX requires tags to be explicitly closed : self-closing tags like <img> must become <img/> and wrapping tags like <li>oranges must be written as <li>oranges</li>
EX :
<>
<img src=”…” alt=”…” class=”…”/>
<ul>
<li>….</li>
<li>….</li>
<li>….</li>
</ul>
</>
3.camelCase ALL MOST OF THE THINGS!
EX :
<img src="…" alt="…" className="…"/>
JS IN JSX WITH CURLY BRACES
PASSING STRINGS WITH QUOTES :
When you want to pass a string attribute to JSX , you put it in single or double quotes
EX :
export default function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/7vQD0fPs.jpg"
alt="Gregorio Y. Zara"
/>
);
}
Here https://i.imgur.com/7vQD0fPs.jpg and “Gregorio Y. Zara” are being passed as an Strings
But what if you want to dynamically specify the src or alt text?
We could use a value from JS by replacing “and” with {and}
EX :
export default function Avatar() {
const avatar = 'https://i.imgur.com/7vQD0fPs.jpg';
const description = 'Gregorio Y. Zara';
return (
<img
className="avatar"
src={avatar}
alt={description}
/>
);
}
Notice the Difference between className=”avatar” which specifies an “avatar” CSS class name that makes the image round and src={avatar} that reads the value of the JS variable called avatar
USE CURLY BRACES : A WINDOW INTO THE JS WORLD
JSX is an Special way of writing JS , its possible to use JS inside it curly braces { }
EX :
export default function TodoList() {
const name = 'Gregorio Y. Zara';
return (
<h1>{name}'s To Do List</h1>
);
}
Any JS expression will work between curly braces , including function calls like formateDate();
EX :
const today = new Date();
function formatDate(date) {
return new Intl.DateTimeFormat(
'en-US',
{ weekday: 'long' }
).format(date);
}
export default function TodoList() {
return (
<h1>To Do List for {formatDate(today)}</h1>
);
}
WHERE TO USE THE CURLY BRACES :
We can use the Curly braces in two ways
1.ASTEXT : we can use here directly a JSX tag –
EX :
<h1>{name}'s To Do List</h1> works, but <{tag}>Gregorio Y. Zara's To Do List</{tag}>
Will not
2.ASATTRIBUTES : immediately following the = sign : src={avatar} will read the avatar variable , but src=”{avatar}” will pass the String “{avatar}”
USING “DOUBLE CURLIES”: CSS AND OTHER OBJECTS IN JSX
Objects also denoted with the Curly braces like {name:”George”,inventions:5} , Therefore to pass a JS object in JSX you must wrap the Object in another pair of curly braces : person={{name:”George”,inventions:5}}
PASSING PROPS TO A COMPONENT :
React components use Props to communicate with each other
Every parent component can pass some information to its child component by giving them Props
We can pass any JS values through them , like objects, arrays and functions
FAMILIRA PROPS :
Props are the information that you pass a JSX tag
EX : className,src,alt,width and height are some of the props we can pass to an <img>
function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/1bX5QH6.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}
export default function Profile() {
return (
<Avatar />
);
}
In this code The Profile component isn’t passing any props to its child component Avatar ,
export default function Profile() {
return (
<Avatar />
);
}
STEP 1 : PASS PROPS TO THE CHILD COMPONENTS
First pass some props to Avatar, EX : lets pass two props : person(an object) and size(a number)
EX :
export default function Profile() {
return (
<Avatar
Person={{name:’Lin Lanying’ , imageId:’1bx5WH6’}}
Size={100}
/>
);
}
STEP 2 : READ PROPS INSIDE THE CHILD COMPONENT
We can read these props by listening their names person,size separated by the commas inside ({ and}) directly function Avatar
EX :
function Avatar({person,size}){
}
Add some logic to Avatar that uses the person and size props for rendering
EX :
import { getImageUrl } from './utils.js';
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function Profile() {
return (
<div>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
<Avatar
size={80}
person={{
name: 'Aklilu Lemma',
imageId: 'OKS67lh'
}}
/>
<Avatar
size={50}
person={{
name: 'Lin Lanying',
imageId: '1bX5QH6'
}}
/>
</div>
);
}
SPECIFYING A DEFAULT VALUE FOR A PROP
EX :
function Avatar({person,size=100}){
}
FORWARDING PROPS WITH THE JSX SPREAD SYNTAX
EX :
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
CONDITIONAL RENDERING :
Conditional render JSX using JS syntax like if statements , && , and ? : operators
EX :
function Item({ name, isPacked }) {
return <li className="item">{name}</li>;
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
isPacked={true}
name="Space suit"
/>
<Item
isPacked={true}
name="Helmet with a golden leaf"
/>
<Item
isPacked={false}
name="Photo of Tam"
/>
</ul>
</section>
);
}
Notice that some of the Iteam components have their isPacked props set to true instead of false
EX :
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
CONDITIONALLY RETURNING NOTHING WITH NULL
In some times you won’t want to render anything at all , a component must return something In this case you can return null
if(isPacked){
return null;
}
return <li className=”item”>{name}</li>
CONDITIONAL(TERNARY) OPERATOR (? : )
EX :
if(isPacked){
return <li className=”item”>{fname}</li>;
}
return <li className=”item”>{name}</li>;
LOGICAL AND OPERATOR(&&)
return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
RENDERING LISTS :
If you want to display multiple similar components from a collection of data
You can use the JS array method to manipulate an array of data
You can use filter() and map() with React to filter and transform your array of data into an array of components
RENDERING DATA FROM ARRAYS :
EX :
<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>
You can store the data in JS objects and arrays and use the method like map() and filter() to render list of components from them
1.MOVE the DATA INTO AN ARRAY
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
2.MAP THE people MEMBERS INTO A NEW ARRAY OF JSX NODES listItems:
const listItems = people.map(person => <li>{person}</li>);
3.RETURN listItems FROM YOUR COMPONENT WRAPPED IN A <ul>:
return <ul>{listItems}</ul>;
Result :
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
export default function List() {
const listItems = people.map(person =>
<li>{person}</li>
);
return <ul>{listItems}</ul>;
}
FILTERING ARRAY OF ITEMS :
const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];
In that above data we want to filter only the specific value like example we want to filter the iteam profession is chemist the test functiob for this like
(person)=>person.profession===’chemist’
1.Create a new array of just “chemist” people, chemists by calling filter() on the people filtering by person.profession===’chemist’
EX :
const chemists = people.filter(person =>
person.profession === 'chemist'
);
2.Now map over chemists :
EX :
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
3.Lastly, return the listItems from your component
return <ul>{listItems}</ul>;
KEEPING COMPONENTS PURE :
Purity : Components as formulas
A pure function is a function with the following characteristics :
1.itminds its own business : it does not change any objects or variables that existed before it was called
2.same inputs , same output : given the same inputs a pure function should always return the same result
EX :
function double(number){
return 2*number;
}
In the above example double is a pure function , if you pass it 3 it will return 6 always
React assumes that every component you write is a pure function
This means that react components you write must always return the same JSX given the same inputs
EX :
function Recipe({ drinkers }) {
return (
<ol>
<li>Boil {drinkers} cups of water.</li>
<li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
<li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
</ol>
);
}
export default function App() {
return (
<section>
<h1>Spiced Chai Recipe</h1>
<h2>For two</h2>
<Recipe drinkers={2} />
<h2>For a gathering</h2>
<Recipe drinkers={4} />
</section>
);
}
When you pass drinkers={2} to Recipe, it will return JSX containing 2 cups of water. Always.
If you pass drinkers={4}, it will return JSX containing 4 cups of water. Always.
UNDERSTANDING YOUR UI AS A TREE :
Trees are a relationship model between items and UI is often represented using the Tree Structures
EX : browsers use tree structures to model HTML(DOM) and CSS(CSSOM)
Trees are a common way to represent the relationship between entities. They are often used to model UI.
Render trees represent the nested relationship between React components across a single render.
With conditional rendering, the render tree may change across different renders. With different prop values, components may render different children components.
Render trees help identify what the top-level and leaf components are. Top-level components affect the rendering performance of all components beneath them and leaf components are often re-rendered frequently. Identifying them is useful for understanding and debugging rendering performance.
Dependency trees represent the module dependencies in a React app.
Dependency trees are used by build tools to bundle the necessary code to ship an app.
Dependency trees are useful for debugging large bundle sizes that slow time to paint and expose opportunities for optimizing what code is bundled.
ADDING INTERACTIVITY :
Responding to events :
React lets you add event handlers to your JSX ,Event handlers are your own functions that will be triggerd in response to user interactions like clicking , hovering , focusing , on form inputs etc..
Built-in components like <button> only support built-in browser events like onClick
EX :
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing!')}
onUploadImage={() => alert('Uploading!')}
/>
);
}
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
</div>
);
}
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
RENDER AND COMMIT :
Before your components are displayed on the screen they must be rendered by React
1.Triggering : a render (delivering the diner’s order to the kitchen)
2.Rendering : the component(preparing the order in the kitchen)
3.Commiting : to the DOM(placing the order on the table)
RESPONDING TO EVENTS :
React lets you add event handlers to your JSX , Event handlers are your own functions that will be triggred in response to interactions like clicking,hovering, focusing from inputs and so on
EX :
export default function Button() {
return (
<button>
I don't do anything
</button>
);
}
You can make it show an message when a user clicks by following these 3 steps
1.Declare a function called handleClick inside your Button component
2.Implememt the logic inside that function (use alert to show the message)
3.Add onClick={handleClick} to the <button> JSX
EX :
export default function Button() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
Alternatively you can define an event handler inline in the JSX
<button onClick={function handleClick(){
alert('You clicked me!');
}}>
READING PROPS IN EVENT HANDLERS :
Event handlers are declared inside of a component , they have access to the component’s props
Here is a button that when clicked shows an alert with its message prop:
EX :
function AlertButton({ message, children }) {
return (
<button onClick={() => alert(message)}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div>
<AlertButton message="Playing!">
Play Movie
</AlertButton>
<AlertButton message="Uploading!">
Upload Image
</AlertButton>
</div>
);
}
PASSING EVENT HANDLERS AS PROPS :
EX :
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
function PlayButton({ movieName }) {
function handlePlayClick() {
alert(`Playing ${movieName}!`);
}
return (
<Button onClick={handlePlayClick}>
Play "{movieName}"
</Button>
);
}
function UploadButton() {
return (
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
);
}
export default function Toolbar() {
return (
<div>
<PlayButton movieName="Kiki's Delivery Service" />
<UploadButton />
</div>
);
}
PlayButton passes handlePlayClick as the onClick prop to the Button inside
UploadButton passes ()=> alert(‘Uploading!’) as the onClick prop to the Button inside
EVENT PROPOGATION :
Event handlers will also catch events from any children your component might have We say that an event “bubbles” or “propagets” up the tree
This <div> contains two buttons Both the <div> and each button have their own onClick handlers
EX :
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<button onClick={() => alert('Playing!')}>
Play Movie
</button>
<button onClick={() => alert('Uploading!')}>
Upload Image
</button>
</div>
);
}
If you click on either button , its onClick will run first , followed by the parent <div>’s onClick , so two message will appear
STOPPING PROPOGATION :
When you want to stop your propagation use e.stopPropagation()
EX :
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<Button onClick={() => alert('Playing!')}>
Play Movie
</Button>
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
</div>
);
}
When you click on a button:
React calls the onClick handler passed to <button>.
That handler, defined in Button, does the following:
Calls e.stopPropagation(), preventing the event from bubbling further.
Calls the onClick function, which is a prop passed from the Toolbar component.
That function, defined in the Toolbar component, displays the button’s own alert.
Since the propagation was stopped, the parent <div>’s onClick handler does not run.
As a result of e.stopPropagation(), clicking on the buttons now only shows a single alert (from the <button>) rather than the two of them (from the <button> and the parent toolbar <div>). Clicking a button is not the same thing as clicking the surrounding toolbar, so stopping the propagation makes sense for this UI.
PASSING HANDLERS AS ALTERNATIVE TO PROPOGATION
EX :
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
You can handle events by passing a function as a prop to an element like <button>.
Event handlers must be passed, not called! onClick={handleClick}, not onClick={handleClick()}.
You can define an event handler function separately or inline.
Event handlers are defined inside a component, so they can access props.
You can declare an event handler in a parent and pass it as a prop to a child.
You can define your own event handler props with application-specific names.
Events propagate upwards. Call e.stopPropagation() on the first argument to prevent that.
Events may have unwanted default browser behavior. Call e.preventDefault() to prevent that.
Explicitly calling an event handler prop from a child handler is a good alternative to propagation.
STATE : A COMPONENT’S MEMORY
When A Regular Variable isn’t Enough
Here component that renders a sculpture images. Clicking the NEXT button should show the next sculpture by changing the index to 1 then 2 etc.. , Its won’t work
EX :
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index = index + 1;
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img
src={sculpture.url}
alt={sculpture.alt}
/>
<p>
{sculpture.description}
</p>
</>
);
}
EXPLANATION : The handleClick event handler is updating a local variable , index . But two things prevent that change from being visible
1.Local variable don’t persist between renders : when React renders this component a second time , it renders it from scratch- it doesn’t consider any changes to the local variable
2.Changes to Local Variables won’t trigger renders : React doesn’t realize ir needs to render the component again with the new data
To update a component with new data , two things need to happen
1.Retain the data between renders
2.Triggers React to render the component with new data(re-rendering)
The useState Hook provides those two things :
1.A state variable to retain the data between renders
2.A state setter function to update the variable and trigger React to render the component again
ADDING A STATE VARIABLE :
To add a state variable import useState from react at the top of the file
Import {useState} from ‘react’;
Then , replace this line :
Let index=0;
With that
Const[index,setIndex]=useState(0);
Index is an state variable and setIndex is the setter function
Mofified code is,
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img
src={sculpture.url}
alt={sculpture.alt}
/>
<p>
{sculpture.description}
</p>
</>
);
}
MEET YOUR FIRST HOOK
In React , useState as well as any other function starting with “use” is called Hook
Hooks are special function that are only available while React is rendering
Abatomy of useState :
When you call useState you are telling React that you want this component to remember something
Const[index,setIndex]=useState(0);
In this case , you want React to remember index
Every time your component renders, useState gives you an array containing two values :
1.The state variable(index) with the value you stored
2.The state setter function(setIndex) which can update the state variable and trigger React to render the component again
Const[index,setIndex]=useState(0);
1.Your component renders the First time . Bcz you passed 0 to useState as the initial value for index , it will return [0,setIndex] React remembers 0 is the latest state value
2.you update the state when a user clicks the button it calls setIndex(index+1) , index is 0, so its setIndex(1). This tells React to remember index is 1 now and triggers another render
3.your component’s second render . React still sees useState(0), but because React rembers that you set index to 1 , it returns [1,setIndex] instead
GIVING A COMPONENT MULTIPLE STATE VARIABLES
You can have as many state variables of as many types as you like in one component ,
This component has two state variables , a number index and a Boolean showMore that’s toggles when you click “Show Details”
EX :
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
function handleNextClick() {
setIndex(index + 1);
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<button onClick={handleMoreClick}>
{showMore ? 'Hide' : 'Show'} details
</button>
{showMore && <p>{sculpture.description}</p>}
<img
src={sculpture.url}
alt={sculpture.alt}
/>
</>
);
}
STATE IS ISLOATED AND PRIVATE
State is a local to a component instance on the screen , in other words “if you render the same component twice , each copy will have completely isolated state!” changing one of them will not affect the other
In this example the Gallery component from earlier is renderd twice with no changes to its logic , try clicking the buttons inside each of the galleries
EX :
import Gallery from './Gallery.js';
export default function Page() {
return (
<div className="Page">
<Gallery />
<Gallery />
</div>
);
}
RENDER AND COMMIT :
Before your components are disaplayed on screen they must be rendered by React
There are 3 steps have to follow
1.Triggering : a render (delivering the guest’s order to the kitchen)
2.Rendering : the component(preparing the order in the kitchen)
3.Commiting : to the the DOM(placing the order on the table)
STEP 1 : TRIGGER A RENDER
There are two reasons for a computer to render :
1.It’s the component’s initial render
2.The component’s (or one of its ancestors) state has been updated
INITIAL RENDER :
When your app starts you need to trigger the initial render , Frameworks and sandboxes sometimes hide this code
But its done by calling createRoot with the target DOM node , and then calling its render method with your component
INDEX.JS
import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Image />);
=============================================================================
IMAGE.JS
export default function Image() {
return (
<img
src="https://i.imgur.com/ZF6s192.jpg"
alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
/>
);
}
Re-Renders when State Updates :
Once the component has been initially rendered, you can trigger further renders by updating its state with the set function
Updating your component’s state automatically queues a render
STEP 2: REACT RENDERS YOUR COMPONENTS
Rendering is React calling your components
On initial render : React will call the Root Component
For Subsequent renders : React will call the function component whose state update triggered the render
This process is recursive: if the updated component returns some other component, React will render that component next, and if that component also returns something, it will render that component next, and so on. The process will continue until there are no more nested components and React knows exactly what should be displayed on screen.
EX :
In this Example React will call Gallery() and Image() several times\
INDEX.JS
import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Gallery />);
===============================================================================
GALLERY.JS
export default function Gallery() {
return (
<section>
<h1>Inspiring Sculptures</h1>
<Image />
<Image />
<Image />
</section>
);
}
function Image() {
return (
<img
src="https://i.imgur.com/ZF6s192.jpg"
alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
/>
);
}
During the initial Render , React will Create the DOM nodes for <section>,<h1> and three <img> tags
During a Re-Render , React will calculate which of their properties , if any have changed since the previous render. It won’t do anything with that information until the next step commit phase
STEP 3 : REACT COMMITS CHANGES TO THE DOM
After rendering(calling) your components , React will modify the DOM
For The Initial Render : React will use the appendChild() DOM API to put all the DOM nodes it has created on screen
For Re-Renders : React will apply the minimal necessary operations (calculated while rendering) to make the DOM matches the latest rendering output
React only changes the DOM nodes if there’s an difference between renders
EX : Component that re-renders with different props passed from its parent every second
Notice how you can add some text into the <input> updating its value , but the text doesn’t disappear when the component re-renders
EX :
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}
This works because during this last step,React only updates the content of <h1> with the new time
It sees that the <input> appears in the JSX in the same place as last time So React Doesn’t touch the <input> or its value
You can use the Strict Mode to find mistakes in your components
React does not touch the DOM if the rendering result is the same as last time
STATE AS A SNAPSHOT :
State variable might look like regulr JS variables that you can read and write to
SETTING STATE TRIGGERS RENDERS :
You might think of your user interface as changing directly in response to the user event like a click. In React, it works a little differently from this mental model. On the previous page, you saw that setting state requests a re-render from React. This means that for an interface to react to the event, you need to update the state.
In this example, when you press “send”, setIsSent(true) tells React to re-render the UI:
EX :
import { useState } from 'react';
export default function Form() {
const [isSent, setIsSent] = useState(false);
const [message, setMessage] = useState('Hi!');
if (isSent) {
return <h1>Your message is on its way!</h1>
}
return (
<form onSubmit={(e) => {
e.preventDefault();
setIsSent(true);
sendMessage(message);
}}>
<textarea
placeholder="Message"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Send</button>
</form>
);
}
function sendMessage(message) {
// ...
}
Here’s what happens when you click the button:
The onSubmit event handler executes.
setIsSent(true) sets isSent to true and queues a new render.
React re-renders the component according to the new isSent value.
RENDERING TAKES A SNAPSHOT IN TIME :
“Rendering” means that React is calling your component, which is a function. The JSX you return from that function is like a snapshot of the UI in time. Its props, event handlers, and local variables were all calculated using its state at the time of the render.
When React re-renders a component:
React calls your function again.
Your function returns a new JSX snapshot.
React then updates the screen to match the snapshot your function returned.
As a component’s memory, state is not like a regular variable that disappears after your function returns. State actually “lives” in React itself—as if on a shelf!—outside of your function. When React calls your component, it gives you a snapshot of the state for that particular render. Your component returns a snapshot of the UI with a fresh set of props and event handlers in its JSX, all calculated using the state values from that render!
You tell React to update the state à React updates the state value à React passes a snapshot of the state value into the Component
Here’s a little experiment to show you how this works. In this example, you might expect that clicking the “+3” button would increment the counter three times because it calls setNumber(number + 1) three times.
See what happens when you click the “+3” button:
EX :
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
Notice that number only increments once per click!
Setting state only changes it for the next render. During the first render, number was 0. This is why, in that render’s onClick handler, the value of number is still 0 even after setNumber(number + 1) was called:
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
Here is what this button’s click handler tells React to do:
setNumber(number + 1): number is 0 so setNumber(0 + 1).
React prepares to change number to 1 on the next render.
setNumber(number + 1): number is 0 so setNumber(0 + 1).
React prepares to change number to 1 on the next render.
setNumber(number + 1): number is 0 so setNumber(0 + 1).
React prepares to change number to 1 on the next render.
Even though you called setNumber(number + 1) three times, in this render’s event handler number is always 0, so you set the state to 1 three times. This is why, after your event handler finishes, React re-renders the component with number equal to 1 rather than 3.
You can also visualize this by mentally substituting state variables with their values in your code. Since the number state variable is 0 for this render, its event handler looks like this:
<button onClick={() => {
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
}}>+3</button>
For the next render, number is 1, so that render’s click handler looks like this:
<button onClick={() => {
setNumber(1 + 1);
setNumber(1 + 1);
setNumber(1 + 1);
}}>+3</button>
This is why clicking the button again will set the counter to 2, then to 3 on the next click, and so on.
STATE OVER TIME :
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
alert(number);
}}>+5</button>
</>
)
}
If you use the substitution method from before, you can guess that the alert shows “0”:
setNumber(0 + 5);
alert(0);
QUEUEING A SERIES OF STATE UPDATES :
Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render.
REACT BATCHES STATE UPDATES
You might expect that clicking the “+3” button will increment the counter three times because it calls setNumber(number + 1) three times:
EX :
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
However, as you might recall from the previous section, each render’s state values are fixed, so the value of number inside the first render’s event handler is always 0, no matter how many times you call setNumber(1):
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
But there is one other factor at play here. React waits until all code in the event handlers has run before processing your state updates. This is why the re-render only happens after all these setNumber() calls.
This might remind you of a waiter taking an order at the restaurant. A waiter doesn’t run to the kitchen at the mention of your first dish! Instead, they let you finish your order, let you make changes to it, and even take orders from other people at the table.
UPDATING THE SAME STATE MULTIPLE TIMES BEFORE THE NEXT RENDER
It is an uncommon use case, but if you would like to update the same state variable multiple times before the next render, instead of passing the next state value like setNumber(number + 1), you can pass a function that calculates the next state based on the previous one in the queue, like setNumber(n => n + 1). It is a way to tell React to “do something with the state value” instead of just replacing it.
Try incrementing the counter now:
EX :
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
Here, n => n + 1 is called an updater function. When you pass it to a state setter:
React queues this function to be processed after all the other code in the event handler has run.
During the next render, React goes through the queue and gives you the final updated state.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
Here’s how React works through these lines of code while executing the event handler:
setNumber(n => n + 1): n => n + 1 is a function. React adds it to a queue.
setNumber(n => n + 1): n => n + 1 is a function. React adds it to a queue.
setNumber(n => n + 1): n => n + 1 is a function. React adds it to a queue.
When you call useState during the next render, React goes through the queue. The previous number state was 0, so that’s what React passes to the first updater function as the n argument. Then React takes the return value of your previous updater function and passes it to the next updater as n, and so on:
queued update n returns
n => n + 1 0 0 + 1 = 1
n => n + 1 1 1 + 1 = 2
n => n + 1 2 2 + 1 = 3
React stores 3 as the final result and returns it from useState.
This is why clicking “+3” in the above example correctly increments the value by 3.
WHAT HAPPENS IF YOU UPDATE STATE AFTER REPLACING IT :
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
EX :
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>Increase the number</button>
</>
)
}
Here’s what this event handler tells React to do:
setNumber(number + 5): number is 0, so setNumber(0 + 5). React adds “replace with 5” to its queue.
setNumber(n => n + 1): n => n + 1 is an updater function. React adds that function to its queue.
During the next render, React goes through the state queue:
queued update n returns
”replace with 5” 0(unused) 5
n => n + 1 5 5 + 1 = 6
React stores 6 as the final result and returns it from useState.
WHAT HAPPENS IF YOU REPLACE STATE AFTER UPDATING IT
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>
EX :
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>Increase the number</button>
</>
)
}
Here’s how React works through these lines of code while executing this event handler:
setNumber(number + 5): number is 0, so setNumber(0 + 5). React adds “replace with 5” to its queue.
setNumber(n => n + 1): n => n + 1 is an updater function. React adds that function to its queue.
setNumber(42): React adds “replace with 42” to its queue.
During the next render, React goes through the state queue:
queued update n returns
”replace with 5” 0(unused) 5
n => n + 1 5 5 + 1 = 6
”replace with 42” 6 (unused) 42
Then React stores 42 as the final result and returns it from useState.
To summarize, here’s how you can think of what you’re passing to the setNumber state setter:
An updater function (e.g. n => n + 1) gets added to the queue.
Any other value (e.g. number 5) adds “replace with 5” to the queue, ignoring what’s already queued.
After the event handler completes, React will trigger a re-render. During the re-render, React will process the queue. Updater functions run during rendering, so updater functions must be pure and only return the result. Don’t try to set state from inside of them or run other side effects. In Strict Mode, React will run each updater function twice (but discard the second result) to help you find mistakes.
UPDATING OBJECTS IN STATE :
State can hold any kind of JavaScript value, including objects. But you shouldn’t change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.
WHAT IS MUTATION ?
You can store any kind of JavaScript value in state.
const [x, setX] = useState(0);
So far you’ve been working with numbers, strings, and booleans. These kinds of JavaScript values are “immutable”, meaning unchangeable or “read-only”. You can trigger a re-render to replace a value:
setX(5);
The x state changed from 0 to 5, but the number 0 itself did not change. It’s not possible to make any changes to the built-in primitive values like numbers, strings, and booleans in JavaScript.
Now consider an object in state:
const [position, setPosition] = useState({ x: 0, y: 0 });
Technically, it is possible to change the contents of the object itself. This is called a mutation:
position.x = 5;
However, although objects in React state are technically mutable, you should treat them as if they were immutable—like numbers, booleans, and strings. Instead of mutating them, you should always replace them.
TREAT STATE AS READ-ONLY
In other words, you should treat any JavaScript object that you put into state as read-only.
This example holds an object in state to represent the current pointer position. The red dot is supposed to move when you touch or move the cursor over the preview area. But the dot stays in the initial position:
EX :
import { useState } from 'react';
export default function MovingDot() {
const [position, setPosition] = useState({
x: 0,
y: 0
});
return (
<div
onPointerMove={e => {
position.x = e.clientX;
position.y = e.clientY;
}}
style={{
position: 'relative',
width: '100vw',
height: '100vh',
}}>
<div style={{
position: 'absolute',
backgroundColor: 'red',
borderRadius: '50%',
transform: translate(${position.x}px, ${position.y}px),
left: -10,
top: -10,
width: 20,
height: 20,
}} />
</div>
);
}
The problem is with this bit of code.
onPointerMove={e => {
position.x = e.clientX;
position.y = e.clientY;
}}
To actually trigger a re-render in this case, create a new object and pass it to the state setting function:
onPointerMove={e => {
setPosition({
x: e.clientX,
y: e.clientY
});
}}
With setPosition, you’re telling React:
Replace position with this new object
And render this component again
Notice how the red dot now follows your pointer when you touch or hover over the preview area:
EX :
import { useState } from 'react';
export default function MovingDot() {
const [position, setPosition] = useState({
x: 0,
y: 0
});
return (
<div
onPointerMove={e => {
setPosition({
x: e.clientX,
y: e.clientY
});
}}
style={{
position: 'relative',
width: '100vw',
height: '100vh',
}}>
<div style={{
position: 'absolute',
backgroundColor: 'red',
borderRadius: '50%',
transform: translate(${position.x}px, ${position.y}px),
left: -10,
top: -10,
width: 20,
height: 20,
}} />
</div>
);
}
COPYING OBJECTS WITH THE SPREAD SYNTAX
In the previous example, the position object is always created fresh from the current cursor position. But often, you will want to include existing data as a part of the new object you’re creating. For example, you may want to update only one field in a form, but keep the previous values for all other fields.
These input fields don’t work because the onChange handlers mutate the state:
EX :
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: 'bhepworth@sculpture.com'
});
function handleFirstNameChange(e) {
person.firstName = e.target.value;
}
function handleLastNameChange(e) {
person.lastName = e.target.value;
}
function handleEmailChange(e) {
person.email = e.target.value;
}
return (
<>
<label>
First name:
<input
value={person.firstName}
onChange={handleFirstNameChange}
/>
</label>
<label>
Last name:
<input
value={person.lastName}
onChange={handleLastNameChange}
/>
</label>
<label>
Email:
<input
value={person.email}
onChange={handleEmailChange}
/>
</label>
<p>
{person.firstName}{' '}
{person.lastName}{' '}
({person.email})
</p>
</>
);
}
For example, this line mutates the state from a past render:
person.firstName = e.target.value;
The reliable way to get the behavior you’re looking for is to create a new object and pass it to setPerson. But here, you want to also copy the existing data into it because only one of the fields has changed:
setPerson({
firstName: e.target.value, // New first name from the input
lastName: person.lastName,
email: person.email
});
You can use the ... object spread syntax so that you don’t need to copy every property separately.
setPerson({
...person, // Copy the old fields
firstName: e.target.value // But override this one
});
Now the form works!
Notice how you didn’t declare a separate state variable for each input field. For large forms, keeping all data grouped in an object is very convenient—as long as you update it correctly!
EX :
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: 'bhepworth@sculpture.com'
});
function handleFirstNameChange(e) {
setPerson({
...person,
firstName: e.target.value
});
}
function handleLastNameChange(e) {
setPerson({
...person,
lastName: e.target.value
});
}
function handleEmailChange(e) {
setPerson({
...person,
email: e.target.value
});
}
return (
<>
<label>
First name:
<input
value={person.firstName}
onChange={handleFirstNameChange}
/>
</label>
<label>
Last name:
<input
value={person.lastName}
onChange={handleLastNameChange}
/>
</label>
<label>
Email:
<input
value={person.email}
onChange={handleEmailChange}
/>
</label>
<p>
{person.firstName}{' '}
{person.lastName}{' '}
({person.email})
</p>
</>
);
}
Note that the ... spread syntax is “shallow”—it only copies things one level deep. This makes it fast, but it also means that if you want to update a nested property, you’ll have to use it more than once.
UPDATING A NESTED OBJECT :
Consider a nested object structure like this:
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
If you wanted to update person.artwork.city, it’s clear how to do it with mutation:
person.artwork.city = 'New Delhi';
But in React, you treat state as immutable! In order to change city, you would first need to produce the new artwork object (pre-populated with data from the previous one), and then produce the new person object which points at the new artwork:
const nextArtwork = { ...person.artwork, city: 'New Delhi' };
const nextPerson = { ...person, artwork: nextArtwork };
setPerson(nextPerson);
Or, written as a single function call:
setPerson({
...person, // Copy other fields
artwork: { // but replace the artwork
...person.artwork, // with the same one
city: 'New Delhi' // but in New Delhi!
}
});
This gets a bit wordy, but it works fine for many cases:
EX :
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value
}
});
}
function handleCityChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
city: e.target.value
}
});
}
function handleImageChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
image: e.target.value
}
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
Treat all state in React as immutable.
When you store objects in state, mutating them will not trigger renders and will change the state in previous render “snapshots”.
Instead of mutating an object, create a new version of it, and trigger a re-render by setting state to it.
You can use the {...obj, something: 'newValue'} object spread syntax to create copies of objects.
Spread syntax is shallow: it only copies one level deep.
To update a nested object, you need to create copies all the way up from the place you’re updating.
To reduce repetitive copying code, use Immer.
UPDATING ARRAYS IN STATE :
Arrays are mutable in JavaScript, but you should treat them as immutable when you store them in state. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array.
UPDATING ARRAYS WITHOUT MUTATION
In JavaScript, arrays are just another kind of object. Like with objects, you should treat arrays in React state as read-only. This means that you shouldn’t reassign items inside an array like arr[0] = 'bird', and you also shouldn’t use methods that mutate the array, such as push() and pop().
Instead, every time you want to update an array, you’ll want to pass a new array to your state setting function. To do that, you can create a new array from the original array in your state by calling its non-mutating methods like filter() and map(). Then you can set your state to the resulting new array.
Here is a reference table of common array operations. When dealing with arrays inside React state, you will need to avoid the methods in the left column, and instead prefer the methods in the right column:
avoid (mutates the array) | prefer (returns a new array) | |
adding | push, unshift | concat, [...arr] spread syntax (example) |
removing | pop, shift, splice | filter, slice (example) |
replacing | splice, arr[i] = ... assignment | map (example) |
sorting | reverse, sort | copy the array first (example) |
ADDING TO AN ARRAY :
Push() will mutate an array which you don’t want
EX :
import { useState } from 'react';
let nextId = 0;
export default function List() {
const [name, setName] = useState('');
const [artists, setArtists] = useState([]);
return (
<>
<h1>Inspiring sculptors:</h1>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => {
artists.push({
id: nextId++,
name: name,
});
}}>Add</button>
<ul>
{artists.map(artist => (
<li key={artist.id}>{artist.name}</li>
))}
</ul>
</>
);
}
Instead, create a new array which contains the existing items and a new item at the end. There are multiple ways to do this, but the easiest one is to use the ... array spread syntax:
setArtists( // Replace the state
[ // with a new array
...artists, // that contains all the old items
{ id: nextId++, name: name } // and one new item at the end
]
);
EX :
Now its works correctly
import { useState } from 'react';
let nextId = 0;
export default function List() {
const [name, setName] = useState('');
const [artists, setArtists] = useState([]);
return (
<>
<h1>Inspiring sculptors:</h1>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => {
setArtists([
...artists,
{ id: nextId++, name: name }
]);
}}>Add</button>
<ul>
{artists.map(artist => (
<li key={artist.id}>{artist.name}</li>
))}
</ul>
</>
);
}
REMOVING FROM AN ARRAY
The easiest way to remove an item from an array is to filter it out. In other words, you will produce a new array that will not contain that item. To do this, use the filter method, for example:
EX :
import { useState } from 'react';
let initialArtists = [
{ id: 0, name: 'Marta Colvin Andrade' },
{ id: 1, name: 'Lamidi Olonade Fakeye'},
{ id: 2, name: 'Louise Nevelson'},
];
export default function List() {
const [artists, setArtists] = useState(
initialArtists
);
return (
<>
<h1>Inspiring sculptors:</h1>
<ul>
{artists.map(artist => (
<li key={artist.id}>
{artist.name}{' '}
<button onClick={() => {
setArtists(
artists.filter(a =>
a.id !== artist.id
)
);
}}>
Delete
</button>
</li>
))}
</ul>
</>
);
}
Click the “Delete” button a few times, and look at its click handler.
setArtists(
artists.filter(a => a.id !== artist.id)
);
Here, artists.filter(a => a.id !== artist.id) means “create an array that consists of those artists whose IDs are different from artist.id”. In other words, each artist’s “Delete” button will filter that artist out of the array, and then request a re-render with the resulting array. Note that filter does not modify the original array.
TRANSFORMING AN ARRAY :
If you want to change some or all items of the array, you can use map() to create a new array. The function you will pass to map can decide what to do with each item, based on its data or its index (or both).
In this example, an array holds coordinates of two circles and a square. When you press the button, it moves only the circles down by 50 pixels. It does this by producing a new array of data using map():
EX :
import { useState } from 'react';
let initialShapes = [
{ id: 0, type: 'circle', x: 50, y: 100 },
{ id: 1, type: 'square', x: 150, y: 100 },
{ id: 2, type: 'circle', x: 250, y: 100 },
];
export default function ShapeEditor() {
const [shapes, setShapes] = useState(
initialShapes
);
function handleClick() {
const nextShapes = shapes.map(shape => {
if (shape.type === 'square') {
// No change
return shape;
} else {
// Return a new circle 50px below
return {
...shape,
y: shape.y + 50,
};
}
});
// Re-render with the new array
setShapes(nextShapes);
}
return (
<>
<button onClick={handleClick}>
Move circles down!
</button>
{shapes.map(shape => (
<div
key={shape.id}
style={{
background: 'purple',
position: 'absolute',
left: shape.x,
top: shape.y,
borderRadius:
shape.type === 'circle'
? '50%' : '',
width: 20,
height: 20,
}} />
))}
</>
);
}
REPLACING ITEMS IN AN ARRAY
It is particularly common to want to replace one or more items in an array. Assignments like arr[0] = 'bird' are mutating the original array, so instead you’ll want to use map for this as well.
To replace an item, create a new array with map. Inside your map call, you will receive the item index as the second argument. Use it to decide whether to return the original item (the first argument) or something else:
EX :
import { useState } from 'react';
let initialCounters = [
0, 0, 0
];
export default function CounterList() {
const [counters, setCounters] = useState(
initialCounters
);
function handleIncrementClick(index) {
const nextCounters = counters.map((c, i) => {
if (i === index) {
// Increment the clicked counter
return c + 1;
} else {
// The rest haven't changed
return c;
}
});
setCounters(nextCounters);
}
return (
<ul>
{counters.map((counter, i) => (
<li key={i}>
{counter}
<button onClick={() => {
handleIncrementClick(i);
}}>+1</button>
</li>
))}
</ul>
);
}
INSERTING INTO AN ARRAY
Sometimes, you may want to insert an item at a particular position that’s neither at the beginning nor at the end. To do this, you can use the ... array spread syntax together with the slice() method. The slice() method lets you cut a “slice” of the array. To insert an item, you will create an array that spreads the slice before the insertion point, then the new item, and then the rest of the original array.
In this example, the Insert button always inserts at the index 1:
EX :
import { useState } from 'react';
let nextId = 3;
const initialArtists = [
{ id: 0, name: 'Marta Colvin Andrade' },
{ id: 1, name: 'Lamidi Olonade Fakeye'},
{ id: 2, name: 'Louise Nevelson'},
];
export default function List() {
const [name, setName] = useState('');
const [artists, setArtists] = useState(
initialArtists
);
function handleClick() {
const insertAt = 1; // Could be any index
const nextArtists = [
// Items before the insertion point:
...artists.slice(0, insertAt),
// New item:
{ id: nextId++, name: name },
// Items after the insertion point:
...artists.slice(insertAt)
];
setArtists(nextArtists);
setName('');
}
return (
<>
<h1>Inspiring sculptors:</h1>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={handleClick}>
Insert
</button>
<ul>
{artists.map(artist => (
<li key={artist.id}>{artist.name}</li>
))}
</ul>
</>
);
}
MAKING OTHER CHANGES TO AN ARRAY
There are some things you can’t do with the spread syntax and non-mutating methods like map() and filter() alone. For example, you may want to reverse or sort an array. The JavaScript reverse() and sort() methods are mutating the original array, so you can’t use them directly.
However, you can copy the array first, and then make changes to it.
For example:
import { useState } from 'react';
const initialList = [
{ id: 0, title: 'Big Bellies' },
{ id: 1, title: 'Lunar Landscape' },
{ id: 2, title: 'Terracotta Army' },
];
export default function List() {
const [list, setList] = useState(initialList);
function handleClick() {
const nextList = [...list];
nextList.reverse();
setList(nextList);
}
return (
<>
<button onClick={handleClick}>
Reverse
</button>
<ul>
{list.map(artwork => (
<li key={artwork.id}>{artwork.title}</li>
))}
</ul>
</>
);
}
UPDATING OBJECTS INSIDE ARRAYS
Objects are not really located “inside” arrays. They might appear to be “inside” in code, but each object in an array is a separate value, to which the array “points”. This is why you need to be careful when changing nested fields like list[0]. Another person’s artwork list may point to the same element of the array!
When updating nested state, you need to create copies from the point where you want to update, and all the way up to the top level. Let’s see how this works.
In this example, two separate artwork lists have the same initial state. They are supposed to be isolated, but because of a mutation, their state is accidentally shared, and checking a box in one list affects the other list:
EX :
import { useState } from 'react';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [myList, setMyList] = useState(initialList);
const [yourList, setYourList] = useState(
initialList
);
function handleToggleMyList(artworkId, nextSeen) {
const myNextList = [...myList];
const artwork = myNextList.find(
a => a.id === artworkId
);
artwork.seen = nextSeen;
setMyList(myNextList);
}
function handleToggleYourList(artworkId, nextSeen) {
const yourNextList = [...yourList];
const artwork = yourNextList.find(
a => a.id === artworkId
);
artwork.seen = nextSeen;
setYourList(yourNextList);
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={myList}
onToggle={handleToggleMyList} />
<h2>Your list of art to see:</h2>
<ItemList
artworks={yourList}
onToggle={handleToggleYourList} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
The problem is in code like this:
const myNextList = [...myList];
const artwork = myNextList.find(a => a.id === artworkId);
artwork.seen = nextSeen; // Problem: mutates an existing item
setMyList(myNextList);
Although the myNextList array itself is new, the items themselves are the same as in the original myList array. So changing artwork.seen changes the original artwork item. That artwork item is also in yourList, which causes the bug. Bugs like this can be difficult to think about, but thankfully they disappear if you avoid mutating state.
You can use map to substitute an old item with its updated version without mutation.
setMyList(myList.map(artwork => {
if (artwork.id === artworkId) {
// Create a new object with changes
return { ...artwork, seen: nextSeen };
} else {
// No changes
return artwork;
}
}));
Here, ... is the object spread syntax used to create a copy of an object.
With this approach, none of the existing state items are being mutated, and the bug is fixed:
EX :
import { useState } from 'react';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [myList, setMyList] = useState(initialList);
const [yourList, setYourList] = useState(
initialList
);
function handleToggleMyList(artworkId, nextSeen) {
setMyList(myList.map(artwork => {
if (artwork.id === artworkId) {
// Create a new object with changes
return { ...artwork, seen: nextSeen };
} else {
// No changes
return artwork;
}
}));
}
function handleToggleYourList(artworkId, nextSeen) {
setYourList(yourList.map(artwork => {
if (artwork.id === artworkId) {
// Create a new object with changes
return { ...artwork, seen: nextSeen };
} else {
// No changes
return artwork;
}
}));
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={myList}
onToggle={handleToggleMyList} />
<h2>Your list of art to see:</h2>
<ItemList
artworks={yourList}
onToggle={handleToggleYourList} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
In general, you should only mutate objects that you have just created. If you were inserting a new artwork, you could mutate it, but if you’re dealing with something that’s already in state, you need to make a copy.
MANAGING STATE
As your application grows, it helps to be more intentional about how your state is organized and how the data flows between your components. Redundant or duplicate state is a common source of bugs
REACTING TO INPUT WITH STATE
With React, you won’t modify the UI from code directly. For example, you won’t write commands like “disable the button”, “enable the button”, “show the success message”, etc. Instead, you will describe the UI you want to see for the different visual states of your component (“initial state”, “typing state”, “success state”), and then trigger the state changes in response to user input. This is similar to how designers think about UI.
Here is a quiz form built using React. Note how it uses the status state variable to determine whether to enable or disable the submit button, and whether to show the success message instead.
EX :
import { useState } from 'react';
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>That's right!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
}
function handleTextareaChange(e) {
setAnswer(e.target.value);
}
return (
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br />
<button disabled={
answer.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<p className="Error">
{error.message}
</p>
}
</form>
</>
);
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = answer.toLowerCase() !== 'lima'
if (shouldError) {
reject(new Error('Good guess but a wrong answer. Try again!'));
} else {
resolve();
}
}, 1500);
});
}
CHOOSING THE STATE STRUCTURE
Structuring state well can make a difference between a component that is pleasant to modify and debug, and one that is a constant source of bugs. The most important principle is that state shouldn’t contain redundant or duplicated information. If there’s unnecessary state, it’s easy to forget to update it, and introduce bugs!
For example, this form has a redundant fullName state variable:
EX :
import { useState } from 'react';
export default function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
setFullName(e.target.value + ' ' + lastName);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
setFullName(firstName + ' ' + e.target.value);
}
return (
<>
<h2>Let’s check you in</h2>
<label>
First name:{' '}
<input
value={firstName}
onChange={handleFirstNameChange}
/>
</label>
<label>
Last name:{' '}
<input
value={lastName}
onChange={handleLastNameChange}
/>
</label>
<p>
Your ticket will be issued to: <b>{fullName}</b>
</p>
</>
);
}
You can remove it and simplify the code by calculating fullName while the component is rendering:
EX :
import { useState } from 'react';
export default function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;
function handleFirstNameChange(e) {
setFirstName(e.target.value);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
}
return (
<>
<h2>Let’s check you in</h2>
<label>
First name:{' '}
<input
value={firstName}
onChange={handleFirstNameChange}
/>
</label>
<label>
Last name:{' '}
<input
value={lastName}
onChange={handleLastNameChange}
/>
</label>
<p>
Your ticket will be issued to: <b>{fullName}</b>
</p>
</>
);
}
This might seem like a small change, but many bugs in React apps are fixed this way.
SHARING STATE BETWEEN COMPONENTS
Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as “lifting state up”, and it’s one of the most common things you will do writing React code.
In this example, only one panel should be active at a time. To achieve this, instead of keeping the active state inside each individual panel, the parent component holds the state and specifies the props for its children.
EX :
import { useState } from 'react';
export default function Accordion() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel
title="About"
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
function Panel({
title,
children,
isActive,
onShow
}) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={onShow}>
Show
</button>
)}
</section>
);
}
PRESERVING AND RESETTING STATE
When you re-render a component, React needs to decide which parts of the tree to keep (and update), and which parts to discard or re-create from scratch. In most cases, React’s automatic behavior works well enough. By default, React preserves the parts of the tree that “match up” with the previously rendered component tree.
However, sometimes this is not what you want. In this chat app, typing a message and then switching the recipient does not reset the input. This can make the user accidentally send a message to the wrong person:
EX :
APP.JS
import { useState } from 'react';
import Chat from './Chat.js';
import ContactList from './ContactList.js';
export default function Messenger() {
const [to, setTo] = useState(contacts[0]);
return (
<div>
<ContactList
contacts={contacts}
selectedContact={to}
onSelect={contact => setTo(contact)}
/>
<Chat contact={to} />
</div>
)
}
const contacts = [
{ name: 'Taylor', email: 'taylor@mail.com' },
{ name: 'Alice', email: 'alice@mail.com' },
{ name: 'Bob', email: 'bob@mail.com' }
];
CONTACTLIST.JS
export default function ContactList({
selectedContact,
contacts,
onSelect
}) {
return (
<section className="contact-list">
<ul>
{contacts.map(contact =>
<li key={contact.email}>
<button onClick={() => {
onSelect(contact);
}}>
{contact.name}
</button>
</li>
)}
</ul>
</section>
);
}
CHAT.JS
import { useState } from 'react';
export default function Chat({ contact }) {
const [text, setText] = useState('');
return (
<section className="chat">
<textarea
value={text}
placeholder={'Chat to ' + contact.name}
onChange={e => setText(e.target.value)}
/>
<br />
<button>Send to {contact.email}</button>
</section>
);
}
REACTING TO INPUT WITH STATE
React provides a declarative way to manipulate the UI. Instead of manipulating individual pieces of the UI directly, you describe the different states that your component can be in, and switch between them in response to the user input.
HOW DECLARATIVE UI COMPARES TO IMPERTIVE
When you design UI interactions, you probably think about how the UI changes in response to user actions. Consider a form that lets the user submit an answer:
When you type something into the form, the “Submit” button becomes enabled.
When you press “Submit”, both the form and the button become disabled, and a spinner appears.
If the network request succeeds, the form gets hidden, and the “Thank you” message appears.
If the network request fails, an error message appears, and the form becomes enabled again.
In imperative programming, the above corresponds directly to how you implement interaction. You have to write the exact instructions to manipulate the UI depending on what just happened. Here’s another way to think about this: imagine riding next to someone in a car and telling them turn by turn where to go.
They don’t know where you want to go, they just follow your commands. (And if you get the directions wrong, you end up in the wrong place!) It’s called imperative because you have to “command” each element, from the spinner to the button, telling the computer how to update the UI.
In this example of imperative UI programming, the form is built without React. It only uses the browser DOM:
EX :
INDEX.JS
async function handleFormSubmit(e) {
e.preventDefault();
disable(textarea);
disable(button);
show(loadingMessage);
hide(errorMessage);
try {
await submitForm(textarea.value);
show(successMessage);
hide(form);
} catch (err) {
show(errorMessage);
errorMessage.textContent = err.message;
} finally {
hide(loadingMessage);
enable(textarea);
enable(button);
}
}
function handleTextareaChange() {
if (textarea.value.length === 0) {
disable(button);
} else {
enable(button);
}
}
function hide(el) {
el.style.display = 'none';
}
function show(el) {
el.style.display = '';
}
function enable(el) {
el.disabled = false;
}
function disable(el) {
el.disabled = true;
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
if (answer.toLowerCase() === 'istanbul') {
resolve();
} else {
reject(new Error('Good guess but a wrong answer. Try again!'));
}
}, 1500);
});
}
let form = document.getElementById('form');
let textarea = document.getElementById('textarea');
let button = document.getElementById('button');
let loadingMessage = document.getElementById('loading');
let errorMessage = document.getElementById('error');
let successMessage = document.getElementById('success');
form.onsubmit = handleFormSubmit;
textarea.oninput = handleTextareaChange;
INDEX.HTML
async function handleFormSubmit(e) {
e.preventDefault();
disable(textarea);
disable(button);
show(loadingMessage);
hide(errorMessage);
try {
await submitForm(textarea.value);
show(successMessage);
hide(form);
} catch (err) {
show(errorMessage);
errorMessage.textContent = err.message;
} finally {
hide(loadingMessage);
enable(textarea);
enable(button);
}
}
function handleTextareaChange() {
if (textarea.value.length === 0) {
disable(button);
} else {
enable(button);
}
}
function hide(el) {
el.style.display = 'none';
}
function show(el) {
el.style.display = '';
}
function enable(el) {
el.disabled = false;
}
function disable(el) {
el.disabled = true;
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
if (answer.toLowerCase() === 'istanbul') {
resolve();
} else {
reject(new Error('Good guess but a wrong answer. Try again!'));
}
}, 1500);
});
}
let form = document.getElementById('form');
let textarea = document.getElementById('textarea');
let button = document.getElementById('button');
let loadingMessage = document.getElementById('loading');
let errorMessage = document.getElementById('error');
let successMessage = document.getElementById('success');
form.onsubmit = handleFormSubmit;
textarea.oninput = handleTextareaChange;
In React, you don’t directly manipulate the UI—meaning you don’t enable, disable, show, or hide components directly. Instead, you declare what you want to show, and React figures out how to update the UI. Think of getting into a taxi and telling the driver where you want to go instead of telling them exactly where to turn. It’s the driver’s job to get you there, and they might even know some shortcuts you haven’t considered!
THINKING ABOUT UI DECLARATIVELY
1.Identify your component’s different visual states
2.Determine what triggers those state changes
3.Represent the state in memory using useState
4.Remove any non-essential state variables
5.Connect the event handlers to set the state
STEP 1 : Identify your Component’s different visual states
First, you need to visualize all the different “states” of the UI the user might see:
Empty: Form has a disabled “Submit” button.
Typing: Form has an enabled “Submit” button.
Submitting: Form is completely disabled. Spinner is shown.
Success: “Thank you” message is shown instead of a form.
Error: Same as Typing state, but with an extra error message.
Just like a designer, you’ll want to “mock up” or create “mocks” for the different states before you add logic. For example, here is a mock for just the visual part of the form. This mock is controlled by a prop called status with a default value of 'empty':
EX :
export default function Form({
status = 'empty'
}) {
if (status === 'success') {
return <h1>That's right!</h1>
}
return (
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form>
<textarea />
<br />
<button>
Submit
</button>
</form>
</>
)
}
You could call that prop anything you like, the naming is not important. Try editing status = 'empty' to status = 'success' to see the success message appear. Mocking lets you quickly iterate on the UI before you wire up any logic. Here is a more fleshed out prototype of the same component, still “controlled” by the status prop:
EX :
export default function Form({
// Try 'submitting', 'error', 'success':
status = 'empty'
}) {
if (status === 'success') {
return <h1>That's right!</h1>
}
return (
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form>
<textarea disabled={
status === 'submitting'
} />
<br />
<button disabled={
status === 'empty' ||
status === 'submitting'
}>
Submit
</button>
{status === 'error' &&
<p className="Error">
Good guess but a wrong answer. Try again!
</p>
}
</form>
</>
);
}
STEP 2: DETERMINE WHAT TRIGGERS THOSE STATE CHANGES
You can trigger state updates in response to two kinds of inputs:
Human inputs, like clicking a button, typing in a field, navigating a link.
Computer inputs, like a network response arriving, a timeout completing, an image loading.
In both cases, you must set state variables to update the UI. For the form you’re developing, you will need to change state in response to a few different inputs:
Changing the text input (human) should switch it from the Empty state to the Typing state or back, depending on whether the text box is empty or not.
Clicking the Submit button (human) should switch it to the Submitting state.
Successful network response (computer) should switch it to the Success state.
Failed network response (computer) should switch it to the Error state with the matching error message.
STEP 3 : REPRESENT THE STATE IN MEMORY WITH useState
Next you’ll need to represent the visual states of your component in memory with useState. Simplicity is key: each piece of state is a “moving piece”, and you want as few “moving pieces” as possible. More complexity leads to more bugs!
Start with the state that absolutely must be there. For example, you’ll need to store the answer for the input, and the error (if it exists) to store the last error:
EX :
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
Then, you’ll need a state variable representing which one of the visual states that you want to display. There’s usually more than a single way to represent that in memory, so you’ll need to experiment with it.
If you struggle to think of the best way immediately, start by adding enough state that you’re definitely sure that all the possible visual states are covered:
const [isEmpty, setIsEmpty] = useState(true);
const [isTyping, setIsTyping] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [isError, setIsError] = useState(false);
Your first idea likely won’t be the best, but that’s ok—refactoring state is a part of the process!
STEP 4: REMOVE ANY NON-ESSENTIAL STATE VARIABLES
You want to avoid duplication in the state content so you’re only tracking what is essential. Spending a little time on refactoring your state structure will make your components easier to understand, reduce duplication, and avoid unintended meanings. Your goal is to prevent the cases where the state in memory doesn’t represent any valid UI that you’d want a user to see. (For example, you never want to show an error message and disable the input at the same time, or the user won’t be able to correct the error!)
Does this state cause a paradox? For example, isTyping and isSubmitting can’t both be true. A paradox usually means that the state is not constrained enough. There are four possible combinations of two booleans, but only three correspond to valid states. To remove the “impossible” state, you can combine these into a status that must be one of three values: 'typing', 'submitting', or 'success'.
Is the same information available in another state variable already? Another paradox: isEmpty and isTyping can’t be true at the same time. By making them separate state variables, you risk them going out of sync and causing bugs. Fortunately, you can remove isEmpty and instead check answer.length === 0.
Can you get the same information from the inverse of another state variable? isError is not needed because you can check error !== null instead.
After this clean-up, you’re left with 3 (down from 7!) essential state variables:
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing'); // 'typing', 'submitting', or 'success'
You know they are essential, because you can’t remove any of them without breaking the functionality.
STEP 5 : CONNECT THE EVENT HANDLERS TO SET STATE
Lastly, create event handlers that update the state. Below is the final form, with all event handlers wired up:
EX :
import { useState } from 'react';
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>That's right!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
}
function handleTextareaChange(e) {
setAnswer(e.target.value);
}
return (
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br />
<button disabled={
answer.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<p className="Error">
{error.message}
</p>
}
</form>
</>
);
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = answer.toLowerCase() !== 'lima'
if (shouldError) {
reject(new Error('Good guess but a wrong answer. Try again!'));
} else {
resolve();
}
}, 1500);
});
}
RECAP
Declarative programming means describing the UI for each visual state rather than micromanaging the UI (imperative).
When developing a component:
1.Identify all its visual states.
2.Determine the human and computer triggers for state changes.
3.Model the state with useState.
4.Remove non-essential state to avoid bugs and paradoxes.
5.Connect the event handlers to set state.
CHOSSING THE STATE STRUCTURE
PRINCIPLES FOR STRUCTURING STATE
When you write a component that holds some state, you’ll have to make choices about how many state variables to use and what the shape of their data should be. While it’s possible to write correct programs even with a suboptimal state structure, there are a few principles that can guide you to make better choices:
1.Group related state. If you always update two or more state variables at the same time, consider merging them into a single state variable.
2.Avoid contradictions in state. When the state is structured in a way that several pieces of state may contradict and “disagree” with each other, you leave room for mistakes. Try to avoid this.
3.Avoid redundant state. If you can calculate some information from the component’s props or its existing state variables during rendering, you should not put that information into that component’s state.
4.Avoid duplication in state. When the same data is duplicated between multiple state variables, or within nested objects, it is difficult to keep them in sync. Reduce duplication when you can.
5.Avoid deeply nested state. Deeply hierarchical state is not very convenient to update. When possible, prefer to structure state in a flat way.
The goal behind these principles is to make state easy to update without introducing mistakes. Removing redundant and duplicate data from state helps ensure that all its pieces stay in sync. This is similar to how a database engineer might want to “normalize” the database structure to reduce the chance of bugs. To paraphrase Albert Einstein, “Make your state as simple as it can be—but no simpler.”
SHARING STATE BETWEEN COMPONENTS
LIFTING STATE UP BY EXAMPLE
In this example, a parent Accordion component renders two separate Panels:
Accordion
Panel
Panel
Each Panel component has a boolean isActive state that determines whether its content is visible.
Press the Show button for both panels:
APP.JS
import { useState } from 'react';
function Panel({ title, children }) {
const [isActive, setIsActive] = useState(false);
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={() => setIsActive(true)}>
Show
</button>
)}
</section>
);
}
export default function Accordion() {
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel title="About">
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel title="Etymology">
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
To coordinate these two panels, you need to “lift their state up” to a parent component in three steps:
Remove state from the child components.
Pass hardcoded data from the common parent.
Add state to the common parent and pass it down together with the event handlers.
This will allow the Accordion component to coordinate both Panels and only expand one at a time.
STEP 1: REMOVE STATE FROM THE CHILD COMPONENTS
You will give control of the Panel’s isActive to its parent component. This means that the parent component will pass isActive to Panel as a prop instead. Start by removing this line from the Panel component:
EX :
const [isActive, setIsActive] = useState(false);
And instead, add isActive to the Panel’s list of props:
EX :
function Panel({ title, children, isActive }) {}
Now the Panel’s parent component can control isActive by passing it down as a prop. Conversely, the Panel component now has no control over the value of isActive—it’s now up to the parent component!
STEP 2 : PASS HARDCODED DATA FROM THE COMMON PARENT
To lift state up, you must locate the closest common parent component of both of the child components that you want to coordinate:
Accordion (closest common parent)
Panel
Panel
In this example, it’s the Accordion component. Since it’s above both panels and can control their props, it will become the “source of truth” for which panel is currently active. Make the Accordion component pass a hardcoded value of isActive (for example, true) to both panels:
EX :
import { useState } from 'react';
export default function Accordion() {
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel title="About" isActive={true}>
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel title="Etymology" isActive={true}>
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
function Panel({ title, children, isActive }) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={() => setIsActive(true)}>
Show
</button>
)}
</section>
);
}
Try editing the hardcoded isActive values in the Accordion component and see the result on the screen.
STEP 3 : ADD STATE TO THE COMPONENT PARENT
Lifting state up often changes the nature of what you’re storing as state.
In this case, only one panel should be active at a time. This means that the Accordion common parent component needs to keep track of which panel is the active one. Instead of a boolean value, it could use a number as the index of the active Panel for the state variable:
EX :
const [activeIndex, setActiveIndex] = useState(0);
When the activeIndex is 0, the first panel is active, and when it’s 1, it’s the second one.
Clicking the “Show” button in either Panel needs to change the active index in Accordion. A Panel can’t set the activeIndex state directly because it’s defined inside the Accordion. The Accordion component needs to explicitly allow the Panel component to change its state by passing an event handler down as a prop:
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
The <button> inside the Panel will now use the onShow prop as its click event handler:
EX :
APP.JS
import { useState } from 'react';
export default function Accordion() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel
title="About"
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
function Panel({
title,
children,
isActive,
onShow
}) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={onShow}>
Show
</button>
)}
</section>
);
}
RECAP :
When you want to coordinate two components, move their state to their common parent.
Then pass the information down through props from their common parent.
Finally, pass the event handlers down so that the children can change the parent’s state.
It’s useful to consider components as “controlled” (driven by props) or “uncontrolled” (driven by state).
REACT REFERENCE OVERVIEW
React Hooks :
There are many types of React Hooks available
State Hooks
Context Hooks
Ref Hooks
Effect Hooks
Performance Hooks
Resource Hooks
Other Hooks
React State Hooks :
State Hooks stores and provide access to the information. To add state in Components we use:
useState Hook : useState Hooks provides state variable with direct update access.
useReducer Hook : useReducer Hook provides a state variable along with the update logic in reducer function.
EX :
const [count, setCount] = useState(0)
React Context Hooks :
Context hooks make it possible to access the information without being passed as a prop.
UseContext Hook : shares the data as a global data with passing down props in component tree.
EX :
const context = useContext(myContext);
React Ref Hooks : Refs creates the variable containing the information not used for rendering e.g. DOM nodes.
useRef : Declares a reference to the DOM elements mostly a DOM Node.
useImperativeHandle : It is an additional hook that declares a customizable reference
EX :
const textRef = useRef(null);
Effect Hooks : Effects connect the components and make it sync with the system. It includes changes in browser DOM, networks and other libraries.
useEffect : useEffect Hook connects the components to external system
useLayoutEffect : used to measure the layout, fires when the screen rerenders.
useInsertionEffect : used to insert the CSS dynamically, fires before the changes made to DOM.
EX :
useEffect(()->{
// Code to be executed
}, [dependencies] )
React Performance Hooks : Performace hooks are a way to skip the unnecessary work and optimise the rendering preformance.
useMemo : return a memoized value reducing unnecessary computations.
useCallback : returns a memoized callback that changes if the dependencies are changed.
EX :
const memoizedValue = useMemo(functionThatReturnsValue, arrayDependencies)
To prioritize rendering we can use these :
useTransition : enables us to specify which state changes are critical or urgent and which are not.
useDefferedValue : allows you to postpone the rendering of a component until a specific condition is met.
React Resource Hooks : It allows component to access the resource without being a part of their state e.g., accessing a styling or reading a promise.
Use : used to read the value of resources e.g., context and promises.
EX :
const data = use(dataPromise);
Additional React Hooks : These are rarely used hooks mostly used in libraries.
useDebugValue: helps developers debug custom hooks in React Developer Tools by adding additional information and labels to those hooks.
useID: generates unique IDs i.e, returns a string that is stable across both the server and the client sides.
useSyncExternalStore: helps components subscribe to external stores.
Benefits of using Hooks
Hooks can improve code reusability and make it easier to split complex components into smaller functions.
Simpler, cleaner code: Functional components with hooks are often more concise and easier to understand than class components.
Better for complex UIs: Hooks make it easier to manage state and side effects in components with intricate logic.
Improved maintainability: Code using hooks is often easier to test and debug.
Why the need for ReactJs Hooks?
There are multiple reasons responsible for the introduction of the Hooks which may vary depending upon the experience of developers in developing React application. Needs for react hooks are:
Use of ‘this’ keyword
Reusable stateful logics
Simplifying complex scenarios
1. Use of ‘this’ keyword
Working with classes in React involves understanding JavaScript’s ‘this’ keyword intricacies, causing challenges uncommon in other languages.
Implementing class components requires binding event handlers, adding complexity compared to the simplicity of props and state
React developers note that classes lack efficiency and may hinder hot reloading reliability, a concern Hooks address effectively
2. Reusable stateful logics:
Addressing higher-level concepts like Higher-order components (HOC) and render props, reusing stateful logic is challenging.
Solutions like HOC and render props can lead to an inefficient code base, complicating readability with nested components.
Hooks offer a cleaner way to share stateful logic without altering component hierarchy, enhancing code organization and clarity.
3. Simplifying complex scenarios:
In complex scenarios, life-cycle methods may scatter code, making it challenging to organize related logic in one place.
Hooks address this issue by allowing the organization of code based on related functionality rather than life-cycle methods.
Rules for using Hooks
Only functional components can use hooks
Hooks must be imported from React
Calling of hooks should always be done at top level of components
Hooks should not be inside conditional statements
DIFFRENCE BETWEEN HOOKS AND CLASS COMPONENTS
Feature | Class Components | React Hooks |
State Management | this.state and lifecycle methods | useState and useEffect |
Code Structure | Spread across methods, can be complex | Smaller,focused function |
Reusability | Difficult to reuse logic | Easy to create and reuse custom hooks |
Learning Curve | Familiar to OOP developers | Requires different mindset than classes |
Error Boundaries | Supported | Not currently supported |
Third-Party Libraries | Some libraries rely on them | May not all be compatible yet |
Important things to remember while using hooks :
Hooks are optional in React 16.8+, allowing partial or full project adoption without rewriting existing code.
Hooks are backward-compatible, ensuring smooth integration with existing components and preventing breaking changes.
React has no plans to eliminate classes; Hooks and class components can coexist.
React projects can seamlessly blend class-based and functional components with Hooks.
Hooks provide a direct API for key React concepts, such as props, state, context, refs, and lifecycle.
REACT COMPONENTS:
<Fragment>(<>) : <Fragment>, often used via <>...</> syntax, lets you group elements without a wrapper node.
EX :
<>
<OneChild />
<AnotherChild />
</>
<Fragment>
Wrap elements in <Fragment> to group them together in situations where you need a single element. Grouping elements in Fragment has no effect on the resulting DOM; it is the same as if the elements were not grouped. The empty JSX tag <></> is shorthand for <Fragment></Fragment> in most cases.
Props
optional key: Fragments declared with the explicit <Fragment> syntax may have keys.
Caveats
If you want to pass key to a Fragment, you can’t use the <>...</> syntax. You have to explicitly import Fragment from 'react' and render <Fragment key={yourKey}>...</Fragment>.
React does not reset state when you go from rendering <><Child /></> to [<Child />] or back, or when you go from rendering <><Child /></> to <Child /> and back. This only works a single level deep: for example, going from <><><Child /></></> to <Child /> resets the state. See the precise semantics here.
Usage
Returning multiple elements
Use Fragment, or the equivalent <>...</> syntax, to group multiple elements together. You can use it to put multiple elements in any place where a single element can go. For example, a component can only return one element, but by using a Fragment you can group multiple elements together and then return them as a group:
EX :
function Post() {
return (
<>
<PostTitle />
<PostBody />
</>
);
}
<Profiler> :
<Profiler> lets you measure rendering performance of a React tree programmatically.
EX :
<Profiler id="App" onRender={onRender}>
<App />
</Profiler>
Props
id: A string identifying the part of the UI you are measuring.
onRender: An onRender callback that React calls every time components within the profiled tree update. It receives information about what was rendered and how much time it took.
Caveats
Profiling adds some additional overhead, so it is disabled in the production build by default. To opt into production profiling, you need to enable a special production build with profiling enabled.
onRender callback
React will call your onRender callback with information about what was rendered.
function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {
// Aggregate or log render timings...
}
Parameters
id: The string id prop of the <Profiler> tree that has just committed. This lets you identify which part of the tree was committed if you are using multiple profilers.
phase: "mount", "update" or "nested-update". This lets you know whether the tree has just been mounted for the first time or re-rendered due to a change in props, state, or Hooks.
actualDuration: The number of milliseconds spent rendering the <Profiler> and its descendants for the current update. This indicates how well the subtree makes use of memoization (e.g. memo and useMemo). Ideally this value should decrease significantly after the initial mount as many of the descendants will only need to re-render if their specific props change.
baseDuration: The number of milliseconds estimating how much time it would take to re-render the entire <Profiler> subtree without any optimizations. It is calculated by summing up the most recent render durations of each component in the tree. This value estimates a worst-case cost of rendering (e.g. the initial mount or a tree with no memoization). Compare actualDuration against it to see if memoization is working.
startTime: A numeric timestamp for when React began rendering the current update.
commitTime: A numeric timestamp for when React committed the current update. This value is shared between all profilers in a commit, enabling them to be grouped if desirable.
<StrictMode> :
<StrictMode> lets you find common bugs in your components early during development.
EX :
<StrictMode>
<App />
</StrictMode>
<StrictMode>
Use StrictMode to enable additional development behaviors and warnings for the component tree inside:
EX :
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
);
Strict Mode enables the following development-only behaviors:
Your components will re-render an extra time to find bugs caused by impure rendering.
Your components will re-run Effects an extra time to find bugs caused by missing Effect cleanup.
Your components will be checked for usage of deprecated APIs.
Props
StrictMode accepts no props.
Caveats
There is no way to opt out of Strict Mode inside a tree wrapped in <StrictMode>. This gives you confidence that all components inside <StrictMode> are checked. If two teams working on a product disagree whether they find the checks valuable, they need to either reach consensus or move <StrictMode> down in the tree.
<Suspense> : <Suspense> lets you display a fallback until its children have finished loading.
EX :
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
<Suspense>
Props
children: The actual UI you intend to render. If children suspends while rendering, the Suspense boundary will switch to rendering fallback.
fallback: An alternate UI to render in place of the actual UI if it has not finished loading. Any valid React node is accepted, though in practice, a fallback is a lightweight placeholder view, such as a loading spinner or skeleton. Suspense will automatically switch to fallback when children suspends, and back to children when the data is ready. If fallback suspends while rendering, it will activate the closest parent Suspense boundary.
Caveats
React does not preserve any state for renders that got suspended before they were able to mount for the first time. When the component has loaded, React will retry rendering the suspended tree from scratch.
If Suspense was displaying content for the tree, but then it suspended again, the fallback will be shown again unless the update causing it was caused by startTransition or useDeferredValue.
If React needs to hide the already visible content because it suspended again, it will clean up layout Effects in the content tree. When the content is ready to be shown again, React will fire the layout Effects again. This ensures that Effects measuring the DOM layout don’t try to do this while the content is hidden.
React includes under-the-hood optimizations like Streaming Server Rendering and Selective Hydration that are integrated with Suspense. Read an architectural overview and watch a technical talk to learn more.