Access essential React hooks with this tutorial. Explore the essential React Hooks Cheat Sheet, an invaluable resource providing quick insights for React developers.
Live Codesandbox link with all the examples listed above
SYNTAX:
const [state, setState] = useState(initialValue);
// here state is the value, setState is the state updater function, and
// initial value is given as argument in the useState function
NOTE:
WHY?
const [count, setCount] = useState(0);
const [showText, setShowText] = useState(true);
const buttonHandler = () => {
// here comes the case, as here 2 states are being updated in one function, not a thing to worry,
// but in larger application, you might be changing 5, 10 states in one go, which reduces code redablity and,
// also one useState() declaration per state variable, making the code lengthier and hard to read.
// Therefore, useReducer is used to handle large number of state updations, at once, with ease.
setCount(count + 1);
showText(!showText);
};
<button onClick={buttonHandler}>Update Count</button>;
{
state.showText && <p>This text appears magically! ✨</p>;
}
SYNTAX:
// function which actually updates the state, based on the action wanted
const reducerFunction = (state, action) => {
switch (action.type) {
case "INCREMENT":
// count = count + 1
return { count: state.count + 1, isModalOpen: state.isModalOpen }
case "TOGGLEMODAL":
// toggle modal state
return { count: state.count, isModalOpen: !state.isModalOpen }
default:
return state
}
}
// initial state values
const initialState = {
count: 0,
isModalOpen: true,
}
// state is the object containing state values,
// dispatch is the function, which enables us to change a particular state
const [ state, dispatch ] = useReducer(reducerFunction, intialState)
<p>Button is pressed: {state.count} times</p>
<button>Update Count</button>
{ state.isModalOpen && <Modal>This text appears magically! ✨</Modal> }
NOTE:
SYNTAX:
// generally null is used as initial value, can also leave it as it is
const inputRef = useRef(null);
const buttonHandler = () => {
// clears the git@github.com:daxter-army/react-hooks-cheatsheet.gitinput field value
inputRef.current.value = "";
// focus on the input field
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={buttonHandler}>Note it!</button>
</div>
);
EXTENDED USECASE:
// It can also be used to save some value, but there a catch here
const countRef = useRef(0)
console.log('rendered first time only')
const buttonHandler = () => {
countRef.current++
// updating the ref value do not trigger re-rendering of the component
console.log('value: ', countRef.current)
}
// therefore only initial value stays here, never changes, because the component never re-renders
<p>Button is pressed: {countRef.current}</p>
<button onClick={buttonHandler}>Update Count</button>
NOTE:
External Resources
Dmitri Pavlutin's React ref guide
SYNTAX:
useEffect(() => {
// ...
side_effects;
// ...
return () => {
// preform cleanup here
};
}, [dependency_array]);
EXAMPLE:
const refreshWindowSize = () => {};
useEffect(() => {
// side-effect
window.addEventListener("resize", refreshWindowSize);
// cleanup
return () => window.removeEventListener("resize", refreshWindow);
}, []);
USECASES:
SYNTAX:
useLayoutEffect(() => {
// ...
side-effect
// ...
return(() => { // do cleanup })
}, [dependency_array])
NOTE:
External Resources
Dave Ceddia useLayoutEffect vs useEffect
SYNTAX:
// takes 2 arguments ref and function that returns object
useImperativeHandle(ref, () => ({
alterToggle() {
setToggle(!toggle)
}
}))
NOTE:
WHY?
Parent
/\
/ \
Login User
SYNTAX:
// UsecontextPage.js
import react, { useState, createContext } from "react";
export const AppContext = createContext(null);
const UsecontextPage = () => {
const [username, setUsername] = useState("daxter-army");
return (
<AppContext.Provider value={{ username, setUsername }}>
<Login />
<User />
</AppContext.Provider>
);
};
export default UsecontextPage;
// Login.js
import React, { useContext } from "react";
import { AppContext } from "../../pages/UsecontextPage";
const Login = () => {
const { username, setUsername } = useContext(AppContext);
return (
<input
type="text"
value={username}
onChange={(e) => {
setUsername(e.target.value);
}}
/>
);
};
export default Login;
// User.js
import React, { useContext } from "react";
import { AppContext } from "../../pages/UsecontextPage";
const User = () => {
const { username } = useContext(AppContext);
return <h2>User: {username}</h2>;
};
export default User;
NOTE:
SYNTAX:
const [data, setData] = useState(null);
const [toggle, setToggle] = useState(false);
// data -> It is list of users, which contains info like, user name, user's country, address and geolocation
// data is fetched over an api call and then is used in our app
const getLongestName = (users) => {
if (!users) return null;
// loop...
return longestName;
};
// Now if we update toggle, you know the whole component renders,
// and this getLongestName is also trigerred, although it's input remains the same
// Therefore useMemo, only triggers function, if it is needed to
// useMemo takes 2 arguments, function and dependency array list
const longestUserName = useMemo(() => getLongestUserName(data), [data]);
return (
<div>
<p>Longest Name: {longestUserName}</p>
<button>Toggle</button>
{toggle && "Toggle"}
</div>
);
NOTE:
useCallback v/s useMemo
SYNTAX:
// normal function definition
const getFullName = (firstName, secondName) => firstName + secondName;
// PROBLEM: If parent re-renders, child is gonna get re-render also,
// because the function is also reinitialsed, which is updated for the child and therefore, it also re-renders,
// BUT THIS SHOULD BE AVOIDED
// with useCallback definition
const getFullName = useCallback(
(firstName, secondName) => {
return firstName + secondName;
},
[firstName, secondName]
);
// Now child will only get re-render when firstName, or secondName changes,
// otherwise no re-rendering
return (
<div>
<Child fullName={getFullName} />
</div>
);
NOTE:
SYNTAX:
<!-- index.html -->
... other code
<div id="modal-root"></div>
<div id="root"></div>
... other code
<!-- your usual react code renders inside **#root**, -->
<!-- but the code that we will be rendering with portal would be inside **#modal-root**. -->
// Modal.js
import "./Modal.css";
import { createPortal } from "react-dom";
// It takes 2 arguments, 1st -> the code which is needed to be rendered,
// 2nd the DOM node, in which the code is needed to be rendered.
const Modal = () => {
return createPortal(
<div className="modal">... // modal code</div>,
document.getElementById("modal-root")
);
};
NOTE:
<Parent/>
can captures all the state of the <Modal />
, whether it is implemented using portals or not.It is basically used to share logic across different components, without having to rewrite it.
SYNTAX:
// App.js
return (
<div className="App">
<Products />
<Users />
</div>
);
// Products.js
import withSearch from "../withSearch";
import { products } from "../data";
const Products = ({ data }) => {
return (
<div>
<h3>Products</h3>
{data.map((product) => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
};
export default withSearch(Products, products);
// Users.js
import withSearch from "../withSearch";
import { users } from "../data";
const Users = ({ data }) => {
return (
<div>
<h3>Users</h3>
{data.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
};
export default withSearch(Users, users);
// Otherwise this logic would have been coded in both Products.js and Users.js separately
// withSearch.js
const withSearch = (WrappedComponent, listItems) => {
class HOC extends React.Component {
constructor(props) {
super(props);
this.state = {
searchTerm: "",
};
this.searchTermHandler = this.searchTermHandler.bind(this);
}
searchTermHandler(event) {
this.setState({ searchTerm: event.target.value });
}
render() {
const filteredList = listItems.filter(
(item) =>
item.name
.toLowerCase()
.indexOf(this.state.searchTerm.toLocaleLowerCase()) >= 0
);
return (
<div>
<input
type="text"
placeholder="search"
value={this.searchTerm}
onChange={this.searchTermHandler}
/>
<WrappedComponent data={filteredList} />
</div>
);
}
}
return HOC;
};
export default withSearch;
NOTE:
The core of the HOC can be written as
Do not use call HOC in the render()
render() {
// A new version of EnhancedComponent is created on every render
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// That causes the entire subtree to unmount/remount each time!
return <EnhancedComponent />;
}
React.forwardRef()
const Teacher = lazy(() => import("./Teacher/Teacher"));
const Student = lazy(() => import("./Student/Student"));
const App = () => {
const switchHandler = () => {
setPanel(panel === "TEACHER" ? "STUDENT" : "TEACHER");
};
return (
// ... other code
<button onClick={switchHandler}>Load Panel</button>
<Suspense fallback={<div>loading...</div>}>
{panel === "TEACHER" && <Teacher>Teacher Panel loaded lazily</Teacher>}
{panel === "STUDENT" && <Student>Student Panel loaded lazily</Student>}
</Suspense>
// ... other code
)
}
export default App
Pure Component (React.Memo) is used to optimise your class based/functional component, to prevent unneccessary re-renders, when there is no change in the props of a component.
Need: To optimize unnecessary renders in the components.
A normal component (which is extended from Component class), renders when:
Therefore, by using Pure Component, we can prevent rendering of the component, when the parent renders, and when there is no change in props/state.
How: It shallow compares the prevState with state and prevProps and props, and if there is any difference, then it triggers re-render.
// Parent.js
const [name, setName] = useState("Hello");
useEffect(() => {
const changer = setInterval(() => {
setName("Hello");
console.log("Changing Name!");
}, 2000);
return () => clearInterval(changer);
}, []);
return (
<div>
<RegComp name={name} />
<PureComp name={name} />
</div>
);
// RegComp.js
import { Component } from "react";
class RegComp extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
console.log("RegComp Rendering!");
return (
<p>
<b>Name: {this.props.name}</b>
</p>
);
}
}
export default RegComp;
// PureComp.js
import { PureComponent } from "react";
class PureComp extends PureComponent {
constructor(props) {
super(props);
this.state = {};
}
render() {
console.log("PureComp Rendering!");
return (
<p>
<b>Name: {this.props.name}</b>
</p>
);
}
}
export default PureComp;
Lets consider 2 designs for trains.
<Bike {...props } />
| -> <Body {...props} />
| -> <Chasis {...props} />
| -> <Engine {...props} />
// Like we return <Bike /> to the user,
// and user dont knows about the inner components,
// one single entry point and one large lump
The Second Design: Here every carriage is separated in itself, and all the components are isolated and self-contained, and the carriages are connected with each other with the help of some coupling. The train is not a large lump itself.
<Bike>
<Body />
<Chasis />
<Engine />
</Bike>
// All the components are exposed to the user
<Bike />
is like a magic to the user, but with the compound components design, everything is clear to the user.<Body/>
, then it has to go through <Bike/>
, and is also available to other components regardless of their needs. <Bike {...props } />
|-> <Body {...props} />
|-> <Chasis {...props} />
|-> <Engine {...props} />
But in this design architecture, we can pass specific props to the specific components, even with same names.
<Bike> // --> Locomotive
<Body seats={...} color={...} type={...} /> // ¯\
<Chasis number={...} modelNo={...} /> // > Carriages
<Engine cc={...} oil={...} /> // _/
</Bike>
// Locomotive: We can say that all the logic stays here, and the children component are controlled by it, like select tag in html
// <select>
// <option>One</option>
// <option>Two</option>
// <option>Three</option>
// </select>
// Parent: It is controlling Child
const Parent = () => {
const [value, setValue] = useState("Controlled");
const valueHandler = (inputValue) => {
if (inputValue.match(/^[a-zA-Z]+$/)) {
setValue(inputValue);
}
};
return (
<div>
{/* Controlled Component*/}
<Child textValue={value} textHandler={valueHandler} />
{/* Uncontrolled Component*/}
<Child />
</div>
);
};
// Child: It is being controlled by parent
const Child = ({ textValue, textHandler }) => {
const [text, setText] = useState("Uncontrolled");
const inputHandler = ({ target: { value } }) => {
if (textHandler !== undefined) {
textHandler(value);
} else {
setText(value);
}
};
return (
<input
type="text"
value={textValue !== undefined ? textValue : text}
onChange={inputHandler}
/>
);
};
Source: https://github.com/daxter-army/react-hooks-cheatsheet