React 03: States & Effects
Imports
Importsimport React, { useState, useEffect } from 'react';
Props vs State
Props vs StateThere are two types of “model” data in React:
PropsState
TL;DR
Props: are likeargumentsyou pass to afunction, they let a parentcomponentpass data to a childcomponent.State: is like acomponent’s memory, it lets acomponentkeep track of some information and change it in response to interactions.
Use Cases
Props: customize a childcomponentappearance i.e. aFormcan pass acolorpropto aButton.State: i.e. abuttonto keep track ofisHoveredstate.
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-RenderingRendering: initial render, React calls the
rootcomponent.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 therootcomponent, and its child components recursively.Re-Rendering: React calls thecomponentwhose 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-renderall childcomponentsinside itself
A component re-renders when:
one of its parents gets
re-renderedone of its
propsgets updated (sent from parent)one of its
statesgets 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 StateFiguring 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
stateorpropsin 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+onChangeattributesoninputor other element.Best: use controlled
componentswhenever possible.Why: allow for change-by-change tracking of
inputformvalues, 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()andrefattribute oninputandonchangeattribute onform.Best: if you only need access to the value of the
formon submissionAlways: w files, for
<input>formelements 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→StateState→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 managestateuseEffect(): 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:
useStatecallvalue attribute in
formelementonChangefunction 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
functionhas an implicit parameter (often calledprev) with the latest value of the state.functional updateensures that you are working with the lateststatesnapshot, especially in scenarios wherestateupdates 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 immutabilityState 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
statebased onold state, useprev(functional updates)Derive
statesfrompropswhen possibleRemove unnecessary
statesLift
statesif neededLift
computedvalues if neededDerive
computedvalues 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 RefsSimilar to States:
Refsdo not loose value when the componentre-rendersRefspersists acrossre-renders
But unlike States:
Setting
Refsto new values does NOT triggerre-render.
States:
cause
componentre-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
componentre-evaluationwhen changedCan be used for direct DOM element access (remember
jQueryselectors??)
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;counterlives in the Parent component.All child components (
Incrementer,Decrementer,Resetter) receivesetCountervia 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.
TODO
Debouncing Clicks Actions
Last updated