React.js 03: States & Effects
Debouncing Clicks Actions
TODO
Imports
Imports
import React, { useState, useEffect } from 'react';
Props
vs State
Props
vs State
There are two types of “model” data in React:
Props
State
TL;DR
Props
: are likearguments
you pass to afunction
, they let a parentcomponent
pass data to a childcomponent
.State
: is like acomponent
’s memory, it lets acomponent
keep track of some information and change it in response to interactions.
Use Cases
Props
: customize a childcomponent
appearance i.e. aForm
can pass acolor
prop
to aButton
.State
: i.e. abutton
to keep track ofisHovered
state
.
What is Rendering
?
Rendering
?Rendering
is the process of React
calling your component
to figure out what they display on screen.
Rendering
runs the entire code of a component
.
Rendering
aka Mounting
aka Executing
aka Evaluation
https://react.dev/learn/render-and-commit
Rendering
vs Re-Rendering
Rendering
vs Re-Rendering
Rendering: initial render, React calls the
root
component.Re-Rendering: subsequent renders, React calls component whose state update triggered the render.
Rendering runs the entire code of a component.
Re-Rendering re-runs the entire code of a component.
https://react.dev/learn/render-and-commit
Rendering
, Re-Rendering
And Recursivity
Rendering
, Re-Rendering
And RecursivityRendering
is recursive: if the rendered component returns some other component, React will render that component next, and so on.
Rendering
: React initial render calls theroot
component, and its child components recursively.Re-Rendering
: React calls thecomponent
whose state update triggered the render, and its child components recursively.
Re-Rendering
aka re-executing
aka re-evaluation
aka re-runs
https://react.dev/learn/render-and-commit
Re-Rendering Conditions
By default, when a component re-renders:
React recreates all code defined inside a
component
, includingfunctions
.React will recursively
re-render
all childcomponents
inside itself
A component re-renders
when:
one of its parents gets
re-rendered
one of its
props
gets updated (sent from parent)one of its
states
gets updated
TLDR: Re-Rendering
is tightly related to the Data Flow
, Props
, and States
.
https://react.dev/learn/render-and-commit
Components
Forget Everything
Components
Forget EverythingComponents
have amnesia.
When components
re-render
, all the previous values are lost because it re-runs from scratch.
For states
that persist across re-renders
, we must define them using useState()
.
REACT STATES PERSIST ACROSS RE-RENDERS
State
or not State
State
or not State
Figuring out if data in my app is a state
or not.
Does it remain unchanged over time? If so, it is not a
state
.Is it passed-in from a parent via
props
? If so, it is not astate
.Can you compute it based on existing
state
orprops
in yourcomponent
? If so, it definitely is not astate
.
Controlled Components
- aka form
fields controlled by state
hook
Components
- aka form
fields controlled by state
hookWhen a form
has one or more fields controlled by a state
hook (useState()
), we call it a controlled component
.
Maintains any mutable state
values within the state
property of our components
.
How: using
useState()
and value+onChangeattributes
oninput
or other element.Best: use controlled
components
whenever possible.Why: allow for change-by-change tracking of
input
form
values, they better align with React’s pattern of storing mutable data in acomponent
’s state.
Uncontrolled Component
- uncontrolled form
fields
Component
- uncontrolled form
fieldsA form
element that maintains its own state
in the DOM.
How: uses
useRef()
andref
attribute oninput
andonchange
attribute onform
.Best: if you only need access to the value of the
form
on submissionAlways: w files, for
<input>
form
elements with thetype="file"
attribute.
Re-Rendering
Component
From Within Itself
Re-Rendering
Component
From Within ItselfuseState()
is a React Hook that lets you add a state
variable to your component
.
Once the component
has been initially rendered
, you can trigger further renders
by updating its state
.
React Data Flow
React uses ONE-WAY data flow.
Props
and state
flow DOWN the hierarchy from parent to child component
.
Two-Way Data Binding
React does NOT have built-in two-way data binding
.
But you can achieve the same effect by combining component
state
with event
handlers.
This way, data can flow UP from children components
deep in the hierarchy up to a top component
.
This reverse flow is required to update the component
holding the state
.
import { useState } from "react";
export default function TwoWayBindingExample() {
const [name, setName] = useState("");
return (
<div>
<input
type="text"
value={name} // value comes from state
onChange={(e) => setName(e.target.value)} // updates state when the user types → input and state stay in sync
/>
<p>Hello {name || "Stranger"}</p>
</div>
);
}
This is effectively two-way binding:
Input
→State
State
→Input
Why Use Two-Way Data Binding ?
The two-way data binding pattern
is common in forms
, search bars
, or live previews
.
If you’re building more complex forms, you may want to use 3rd party libraries to simplify two-way binding across many fields.
Props
: Basic Example
Props
: Basic Example const element = <Welcome name="Sara" />;
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
// Equivalent...
function Welcome({name}) { return <h1>Hello, {name}</h1>; }
Props
Forwarding With Spread
Syntax
Props
Forwarding With Spread
SyntaxExample A
const Button = ({ children, ...props }) => {
return (
<button className="someclasses" {...props}>
{children}
</button>
);
};
export default Button;
Use such as
<Button greeting="hey" name="Joe">Click Me</Button>
Example B
export default function Input({richText, ...props}) {
if (richText) return <textarea {...props} />
else return <input {...props} />
}
Use such as
<Input richText="hey this is some fancy text" name="long-text">
<Input value="James" name="fname">
https://react.dev/learn/passing-props-to-a-component#forwarding-props-with-the-jsx-spread-syntax
Hooks: State Hooks, Effect Hooks
Hooks allow us to perform essential logic with our function components
Hooks let us “hook into” the internal component
state
for managing dynamic data in function components
.
useState()
: Hook to managestate
useEffect()
: Hook to execute code after arender
Two main rules to keep in mind when using Hooks:
Only call Hooks from React functions.
Only call Hooks at the top level - never within other functions, conditionals, or loop blocks
State Hook
State lets a component “remember” information like user input.
Updating a state
re-renders your component.
THREE things to setup:
useState
callvalue attribute in
form
elementonChange
function handler
// state setter declaration - useState call
const [phone, setPhone] = useState('');
const [cool, setCool] = useState([]);
const [formState, setFormState] = useState({});
const [categories, setCategories] = useState(null); // for object
// form element declaration for value and onChange
<input name="lastname" onChange={handleLastnameChange} />
<form onSubmit={handleSubmit}><input type="submit" value="Submit" /></form>
State Hook: Updating And Prev Object [Best Practice]
functional update
aka functional form
: passing a function
with prev
to your state
updating function
When updating state
is based on old state
, ALWAYS use a functional update
:
otherwise the update is scheduled for "later"
the updating
function
has an implicit parameter (often calledprev
) with the latest value of the state.functional update
ensures that you are working with the lateststate
snapshot, especially in scenarios wherestate
updates are asynchronous.
When updating state
is NOT based on old state
, NEVER use a functional update
:
not necessary
confusing for other devs reading your code
State Hook: Functional Update When State/Prev Is A Number
functional update
= functional form
.
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((prevCount) => prevCount + 1); // prevCount is whatever is defined in the useState() argument
};
State Hook: Functional Update When State/Prev Is An Object [Best Practice]
functional update
= functional form
.
When State/Prev Is An Object, ALWAYS:
do it IMMUTABLY.
do a deep copy/clone.
Examples below for array, 2D array, shallow object, nest objects.
https://www.udemy.com/course/react-the-complete-guide-incl-redux/learn/lecture/39659798
State Hook: Updating When State Is Array
const [students, setStudents] = useState(["Max","Lauren","Marc"]);
const handleClickAdd = (e) => {
const name = e.target.value;
setStudents((students) => { // Add Item When State Is Array
const newStudents = [...students]; // Clone array for immutability
newStudents.push(name); // Modify array
return newStudents;
});
};
const handleClickRemove = (e) => {
const name = e.target.value;
setStudents((students) => { // Remove Item When State Is Array
const newStudents = [...students]; // Clone array for immutability
const index = newStudents.indexOf(name);
if (index > -1) newStudents.splice(index, 1); // Modify array
return newStudents;
});
};
<button onClick={handleClickAdd} value="Jamie">ADD STUDENT "Jamie"</button>
<button onClick={handleClickRemove} value="Jamie">REMOVE STUDENT "Jamie"</button>
State Hook: Updating When State Is Two Dimensional Array
const gameBoard = [
["max",null,null],
[null,"adri",null],
[null,"james",null],
]
const newBoard = [...gameBoard.map( (array) => [...array] )]; // Clone 2D array for immutability
State Hook: Updating When State Is Shallow Object
const [profile, setProfile] = useState({
name: "James",
city: "London",
});
const handleClick = (keyName, value) => {
setProfile((prev) => ({
...prev, // Clone object for immutability (Shallow Clone)
[keyName]: value, // Add/Update Property
}));
};
<button onClick={() => handleClick("age", "33")}>UPDATE AGE TO 33</button>
<button onClick={() => handleClick("age", "44")}>UPDATE AGE TO 44</button>
State Hook: Updating, State/Prev Is Object With Nested Objects
Assume state is an object with a property 'data'.
const [person, setPerson] = useState({
name: "James",
data: {
dob-year: 1988
}
});
setPerson(person => {
return {
...person,
data: {
...person.data,
someProperty: newValue
}
};
});
State Hook: Avoid Intersecting States [Best Practice]
ALWAYS have a single piece of data managed by a single state only.
Prefer computed
values - keep number of states to a minimum.
State Hook: Misc [Best Practice]
If updating
state
based onold state
, useprev
(functional updates)Derive
states
fromprops
when possibleRemove unnecessary
states
Lift
states
if neededLift
computed
values if neededDerive
computed
values when possible - instead of creating morestates
Effect Hook: Use Cases
Effect Hook = escape hatch
.
Effects are an escape hatch
from the React paradigm.
escape hatches
: features that let you “step outside” React and connect to external systems.
Effects let a component connect to and synchronize
with external systems, with non-React code i.e. deal with network, API calls, DB calls, browser DOM, animations and other non-React code.
Other escape hatches
include refs
, and custom Hooks
.
Effect Hook [Best Practice]
Effect hooks ALWAYS run AFTER a component renders.
One effect hook per process i.e. one API call per effect. NEVER use a single Effect to synchronize several independent processes i.e. two API fetch calls
Don’t use Effects to orchestrate the data flow of your application.
If you’re not interacting with an external system, you might not need an Effect.
If different parts of your Effect should re-run for different reasons, split it into several Effects.
https://react.dev/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things
Effect Hook: How To
Effect hooks ALWAYS run AFTER a component renders.
No Condition: Runs each time a component renders
useEffect(() => {
alert ( "Yo!" );
});
Conditional Re-Render: Runs only ONCE, when the component mounts
useEffect(() => {
alert("component rendered for the first time");
}, []);
Conditional Re-Render: Runs MANY times, when count
or input
changes
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count, input]);
Conditional Re-Render: Cleanup Function: Runs when component unmounts
AND before calling the effect again
useEffect(()=>{
alert ( "Yo!" );
// clean up
return () => {
alert ( "Remove something!" );
};
});
Conditional Re-Render: Cleanup Function: Here used to remove handler
useEffect(()=>{
document.addEventListener('keydown', handleKeyPress);
// clean up
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
});
Functions: Multi line return with JSX
const HeaderComponent = () => {
const classVal = "blue";
return (
<h1 className = {classVal} />
);
}
States
vs Refs
States
vs Refs
Similar to States
:
Refs
do not loose value when the componentre-renders
Refs
persists acrossre-renders
But unlike States
:
Setting
Refs
to new values does NOT triggerre-render
.
States:
cause
component
re-evaluation
(re-renders
) when changedshould be used for values directly reflected in the UI
should NOT be used for "behind the scenes" values wo direct UI impact
Refs
Do NOT cause
component
re-evaluation
when changedCan be used for direct DOM element access (remember
jQuery
selectors??)
https://react.dev/reference/react/useRef
Lifting States
When multiple components can all update the same state, the principle is:
The state lives in the common parent
All children get the state and the updater function via props
Example: Three components updating a counter
import React, { useState } from 'react';
function Incrementer({ counter, setCounter }) {
return <button onClick={() => setCounter(counter + 1)}>Increment</button>;
}
function Decrementer({ counter, setCounter }) {
return <button onClick={() => setCounter(counter - 1)}>Decrement</button>;
}
function Resetter({ setCounter }) {
return <button onClick={() => setCounter(0)}>Reset</button>;
}
function Parent() {
const [counter, setCounter] = useState(0);
return (
<div>
<h1>Counter: {counter}</h1>
<Incrementer counter={counter} setCounter={setCounter} />
<Decrementer counter={counter} setCounter={setCounter} />
<Resetter setCounter={setCounter} />
</div>
);
}
export default Parent;
counter
lives in the Parent component.All child components (
Incrementer
,Decrementer
,Resetter
) receivesetCounter
via props.Any child can update the counter, causing the parent to re-render.
All children always see the latest state, because the parent passes it down again on each render.
Last updated