class-note-content-1735048438814
class-note-content-1735048438814
React Hooks
12.1 What are React hooks, why do we use them and what rules should we follow
when using them?
● What are Hooks? Hooks are JavaScript functions that let you hook onto React state and
lifecycle features inside function components. They let you use state and other React
features without writing a class.
▪ Note: Hooks are backward-compatible, which means it does not contain any
breaking changes. Also, it does not replace your knowledge of React concepts.
▪ Note: Hooks do not work inside classes. Hooks can only live in functional
components and are meant to replace the need for classes
▪ Note: Hooks do not introduce a new concept. They just provide a more direct API
to the things that you already know
● Mismatching Versions of React and React DOM: Hooks are the new feature introduced
in the React 16.8 version, therefore, to use React Hooks, you need to have react 16.8 or
above. Please run this command: npm ls react-dom
● When do we use a Hook? If we write a function component and realize we need to add
some state to it, previously we had to convert it to a class component. Now you can use a
Hook inside the existing function component to add state to your functional component.
● Why use Hooks (the origin of React hooks): Let us discuss the 3 major problems React
Hooks was created to solve to explain the origin of React hooks.
▪ The problem of wrapper hell in class components: Until hooks were introduced,
the main way to inject reusable logic to our component was by wrapping it in
another component, usually achieved by creating a Higher Order Component
(HOC) or utilizing the render props pattern. This makes the components too large.
● With hooks, we can inject a reusable logic into our component without
having to create HOC or use the render props pattern to do it. In short, with
hooks no need to use classes or wrappers, component can simply use the
hook that contains the logic that it needs.
▪ The confusing nature of classes: Classes confuse both people and machines. At
first, the keyword “this” can be confusing to understand. There is also the concept
of bind.
● Hooks let you use more of React’s features without classes. React hooks
embraces functional components and gives them access to important React
features without the worry of writing complex class components.
▪ The issues with huge components: In addition to the confusing nature of classes,
when we deal with state in class components, we can end up with a huge class
component with so much logic and lifecycle methods. In class components, we
need to describe lifecycle methods, state of the component, component’s
functions/methods (that change our state). Because React does no let you sperate
state managements, we can have fetches happen in componentDidMount() and
componentDidUpdate(). But in that same componentDidMount(), we can see
unrelated logic that deals with event listeners. In doing so, classes grow and often
store different logic in one place. Since we use the life cycle methods to organize
states, it is not possible to break components into smaller functionality-based
pieces. Having stateful logic and unrelated code all in one place can be hard to test
for bugs and errors.
● Hooks let you split one component into smaller functions based on what
pieces are related (such as setting up a subscription or fetching data), rather
than forcing a split based on lifecycle methods. This avoids errors.
Example: In class components, data fetching usually happens in the
componentDidMount or componentDidUpdate life cycle methods. Event
listening usually happens inside componentDidMount and event un-
subscription usually happens in componentWillUnmount. Since we use
the life cycle methods to organize states, it is not possible to break
components into smaller functionality-based pieces. Hooks lets you do
that
● React hooks vs React classes: In general, code that you can do in your head is code that
is great. Classes in JavaScript encourage multiple levels of inheritance that quickly increase
overall complexity and potential errors. Anything that allows for composition with plain
functions is a win. Choosing to use stateless functional components removes the risk that
comes with JavaScript classes, constructor initialization and binding functions to
components.
● There are around 10 hooks in React, but we will cover three of the basic hooks in this
course: the useState(), useEffect() and useContext().
● useState(): useState() is a hook that we call it inside a functional component to add some
local state to it. UseState() allows our functional components which used to be stateless
become stateful by allowing us to add local state to a functional component. In short, this
method lets us to Hook into React’s state. Note: useState() hook is the most important and
often used hook.
● Steps we followed when we added the counter state to our class component (revision):
Review the CounterUsingClassState component we did in class.
▪ Step 4: Finally attaching the function with an event in the render() method. In our
case, attaching the function we created to execute when a button is clicked
▪ Step 2/ Import the method useState() from React package: We need a way to
access the React state from a functional component. It is the useState() method lets
you to "Hook into" the React state. That is why we need to import the useState()
method from React into your component. Example:
▪ Step 3/ Initialize state value by calling useState() at the top component: Call
the useState() Hook directly inside our functional component. When you call
useState() on the first line the component becomes a stateful functional component.
● function Example () {
● ... = useState(...);
● }
▪ Step 4/ Initialize state for the component (use destructuring): declare a “state
variable”. Example: Let us declare a state called counter using useState().
useState().
▪ Step 4/ Pass a parameter to useState() to set your sate’s initial value: The only
parameter to useState() is the initial state. No use of “this” or “this.state”. Unlike
with classes, the state does not have to be an object. We can keep a number or a
string as we need. Example: Let us pass 0 as initial state value since we want to
counter to show how many times a user clicked a button:
▪ Step 5/ Update your state using the updater function: When the hook
useState(initialState) is invoked, it returns an array. The first item of this array is
the current state value, and the second item of this array contains a function that
updates the current state. To update the component's state, invoke the updater
function setState(newState) with the new state value. The component re-renders
and state receives the new value newState. Note: You will call the state updater
function from an event handler or somewhere else. Example: Let us update our
count state by increasing the count value by 1 whenever a button is clicked.
▪ Now, recap all the above steps and display the current state:
● function CounterIncrementDecrement() {
● setCount(count + 1);
● };
● return (
● <div>
● ClickToIncreament
● </button>
● </div>
● );
● }
▪ Note: You can call/use the useStae() or useEffect() hooks multiple times in a
single component: So how does the code know which state is corresponding to
which Hook? Hooks look to their order within the component to determine what
state they correspond with, always. Example: Let us declare 2 states (age and
fruit) using useState() and update both states. Because Hooks look to their oder in
the component, the second we call setFruit(), it is known that only the state “fruit”
will update. The other state, “age”, will not update.
● function CounterIncrementDecrement() {
● setFruit("Apple");
● };
● return (
● <div>
● <button onClick={changeMyFruit}>
● ClickForNewFruit
● </button>
● <h1>{fruit}</h1>
● </div>
● );
● }
▪ Call Hooks only at the level in the body of your functional component: Calling
hooks at the top level of your functional component will guarantee that hooks are
called in the same order each time a component renders. Therefore, do not call
hooks inside loops, conditions, any nested function or event handlers.
▪ Call hooks only from React functional components: You cannot use Hooks in
every function, but only in React function components. You cannot even use them
in regular functions within our functional components. Meaning, do not call them
from any regular JavaScript function but only from React functional component.
12.3 Basic React hooks: using useState() to change a state’s value based on previous
state value
● Setting state based on the previous state: If you come across a scenario where the new
state is calculated using the previous state, you can update the state with a callback, it is
always a good idea to pass the value in a callback function as opposed to directly passing
the state variable. We have discussed in the previous class that state update in React was
deliberately made asynchronous to make sure that the state is updated before we use the
updated value in our component. Therefore, your update function will not update your state
with a new one right after you pass your new state in your useState(). If you need to execute
some function using the updated state or to generally verify if the state did indeed update
correctly, make sure to update your state with a callback. Example: If you look at the
example below, we are going to initialize the age state with 5 as value. Let us use a callback
in the updater function to add 5 to the latest age value whenever a button is clicked.
function CounterIncrementDecrement() {
};
return (
<div>
<button onClick={changeMyAge}>
ClickForNewAge
</button>
<h1>{age}</h1>
</div>
);
▪ useState() returns an array of 2 items: the state value and a state updater function.
Example: [state, setState] = useState(initialValue)
▪ Invoking the state updater function setState(newState) with the new value updates
the state. Alternatively, you can invoke the state updater with a callback
setState(prev => next), which returns the new state based on previous.
● Why useEffect(): useEffect() is a React hook that allows our functional components use
the component lifecycle methods (such as, componentDidMount, componentDidUpdate
and componentWillUnmount) which were, in the past, only available for class components.
useEffect() can be used to execute actions when the component mounts, or when certain
prop or state updates or to execute code when the component is about to unmount.
● Understanding useEffect(): We now know that components are used primarily to compute
and render outputs whenever a prop or state of the component changes. However, there are
times when a component makes computations that do not target the change in state/prop
value. These calculations are called side-effects. It is the useEffect() method that we use if
we want to calculate side-effects independent from renderings.
● Explaining what useEffect() does in English: Assume we have a component that has a
count state with 5 as initial value and that the state value increases by 1 whenever a button
is clicked. Assume you want your component to render the updated value upon every click.
The primary job of your component is to calculate the state change and display the updated
state in the browser. However, let us assume you want to run a side effect (changing the
title of your document) independent of what your component renders. This is when you
use useEffect(). Your rendering logic will render your updated count state and your logic
inside useEffect() method will change the title of your document after the component has
rendered.
▪ So, what does useEffect do in short? It allows us to tell our component to execute
some side-effect logic after rendering the component.
▪ Some examples of side effects: Fetching data, updating the DOM, and timers.
● UseEffect() accepts two arguments: a call back and an array containing state variables
▪ The first argument is a callback function: This is where you write your side-
effect logic. Please note that the callback function (your side-effect logic) will
execute after the changes are pushed to DOM (after your component renders).
▪ Example:
• function UsingUseEffect() {
• useEffect(function(){
• alert (“hi”)
• })
• }
● Note: If you do not provide the dependency argument, then, the side effect
runs after every rendering.
▪ function UsingUseEffect() {
▪ useEffect(function(){
▪ alert (“hi”)
▪ })
▪ }
▪ function UsingUseEffect() {
▪ useEffect(function(){
▪ alert (“hi”)
▪ }, [ ])
▪ }
▪ function UsingUseEffect() {
▪ useEffect(function(){
▪ alert (“hi”)
▪ }, [count ])
▪ }
● Now, recap all the above explanations and see how we can use useEffect(): Below is
an example where we have a count state that will increase whenever a button is clicked. In
addition to that we want to create a side effect whereby the title of the document will
display the current count state value upon click.
● function UsingUseEffect() {
● setCount(function (count) {
● return count + 5;
● });
● };
● useEffect(function () {
● return (
● <div>
● <button onClick={countUpdaterFunc}>
● ClickToIncreaseCount
● </button>
● </div>
● );
● }
● function UsingUseEffect() {
● useEffect(() => {
● document.body.style.backgroundColor = color;
● }, [color]);
● return (
● <div>
● <h1>Count : {count}</h1>
● <h1>Age : {age}</h1>
● <button onClick={changeAge}>ClickHere</button>
● </div>
● );
● }
● useEffect cleanup function: Some effects require cleanup to reduce memory leaks.
Timeouts, subscriptions, event listeners, and other effects that are no longer needed should
be disposed. We do this by including a return function (commonly known as useEffect
cleanup function) at the end of the useEffect() hook. The useEffect cleanup is a function
in the useEffect hook that allows us to tidy up our code before our component unmounts.
When our code runs and reruns for every render, useEffect also cleans up after itself using
the cleanup function. Note: Note Do not update the state inside the return/cleanup function.
● useEffect(() => {
● return () => {
● Cleanup code
● }
● }, [stateDependency]);
● Scenarios where and when the useEffect cleanup function useful: Assume we get a
fetch of a particular user through a user’s id, and, before the fetch completes, we change
our mind and try to get another user. At this point, the id, updates while the previous fetch
request is still in progress. This is because useEffect() always attempts to update state of a
component even if the component has been unmounted. As a result, we will get a warning
message that states “Warning: Can’t perform a React state update on unmouted
component”. Therefore, it is then necessary for us to abort the fetch using the cleanup
function, so we do not expose our application to a memory leak.
● Example for useEffect() cleanup function: Let us assume that you have a parent
component called UsingUseEffect.js (that has count as its state) and you want to display 2
child components (Hello.js and Bye.js) in it conditionally. Meaning, assume you want to
display <Hello/> child component in your UsingUseEffect.js if your count state is updated
to a value called “even” when a button is clicked. Assume again that you want to display
the <Bye/> child component if the count state is updated to “odd” upon a click on another
button. The <Bye/> component only renders the “bye” message. The <Hello/> component
has a state called greet and uses useEffect() to display the effect “hello” message after
counting 3 seconds whenever the greet state value updates to “hello”.
▪ Assume that while the <Hello/> component was counting 3 seconds and before it
finishes counting to display the “hello” message, you clicked the odd button. This
is when above “Can’t perform a React state update on an unmounted component”
warning message comes in your console. This warning is because the <Hello/>
component has already been unmouted, but the useEffect() in this component still
is trying to update the greet state of the unmouted component. That is why we need
to cancel any side-effect if a component unmounts.
▪ To cancel the side-effect, we said we can return a cleanup function within our
useEffect() in our <Hello/> component. Basically, we need to clear the timer using
clearTimeout() built in function. Look below for explanation with example:
//Parent component
function UsingUseEffect() {
return (
<div>
Even
</button>
Odd
</button>
</div>
);
}
// Child component (Bye.js)
function Bye() {
return <div>Bye</div>;
function Hello() {
useEffect(() => {
}, 3000);
// cleanup function
}, []);
return (
<div>
</div>
);
● useEffect(() => {
● }, [ ]);
● useEffect(()=>{
● }, [state1]);
● seEffect(() => {
● return () => {
● }, [ ]);
12.5 Basic React hooks: steps to implement context API and useContext() hook
● Questions to ask before diving into React Context API: See below for answers to these
questions.
▪ How about if we directly want to pass data to the grandchild component without
going through the child?
● What is the problem React Context API was created to solve? Prop drilling is the
problem React Context was created to solve
● React Context API: Context is a way to share global data (like props, state or variables)
between deeply nested components more easily without the need to manually pass props
down to each nested component. Note: Each component in Context is context-aware,
therefore, instead of passing props down through every single component on the tree, the
components in need of a prop can simply ask for it, without needing intermediary helper
components that only help relay the prop.
● How does Context API work? The Context API basically lets you broadcast your data to
multiple components by wrapping them with a context provider. It then passes this data to
the context provider using its value attribute. Then child components can tap into this
provider using a context consumer or the useContext() Hook when needed. Note: We will
discuss about consuming data using useContext() hook later.
▪ Step 1: create a context using the createContext() method from React and save it
on a variable. Make sure to export the context you created because in most cases
your component will be in another file. Example:
▪ Step 2: Take your created context and wrap the context provider around the child
components you want to pass down data to. Note: The context you created above,
the “MyContext” , is an object that has two properties, namely Provider and
Consumer. Both Provider and Consumer are components. So, go ahead and wrap
your Provider component around your child component.
▪ Example:
● <MyContext.Provider>
● <ChildComponent/>
● </MyContext.Provider>
▪ Step 3: Put the value you want to pass to any child components on your context
provider using the value prop.
▪ Example:
o <ChildOne />
o </MyContext.Provider>
▪ Step 3: Use the Consumer component to use/consume/read the value of the context
(which you created above) in any child component. Note: To consume the passed
down value, we use a technique called “render props”. Render props is a technique
for sharing data/code between React components using a prop whose value is a
JavaScript function. So, use the Consumer component to wrap this function and
make this function to return the value of your context.
▪ Example:
o MyContext.Consumer>
o {function (value) {
o return <div>{value}</div>;
o }
o }
● Examples on how to use Context API: Before looking at an example on how to avoid
prop drilling using the context API, it is better to see an example on how to pass data to
child components using prop drilling. For both examples below, assume that we are
building an app that welcomes a user by first name when the user logs in. Assume that we
have the root/parent component, called Parent.js where the user’s name object (props) is
available in. However, the component that renders the welcome message with the user’s
name is nested deep within our child component called, ChildThree.js. The user’s name
object will need to be passed down to ChildThree.js component through two other child
components called, ChildOne.js and ChildTwo.js. Now, let us see how we can pass the
user’s name data from Parent.js to ChildThree.js using prop drilling and context API.
● 1. Example of passing data from parent to a child in a deeply nested component (prop
drilling):
// App.js
function App() {
return (
<div>
<MyParent />
</div>
);
// Parent.js
function MyParent() {
return (
<div>
</div>
);
// ChildOne.js
function ChildOne(props) {
return (
<div>
</div>
);
// ChildTwo.js
function ChildTwo(props) {
return (
<div>
</div>
);
// ChildThree.js
function ChildThree(props) {
return
<div>Welcome : {props.user}</div>;
// App.js
import "./App.css";
function App() {
return (
<div>
<MyParent />
</div>
);
//MyParent.js (this is where you provide a context to allow data to pass down to child
components)
function MyParent() {
return (
<div>
<MyContext.Provider value={"Alem"}>
<ChildOne />
</MyContext.Provider>
</div>
);
// ChildOne.js
function ChildOne() {
return (
<div><ChildTwo /></div>
);
// ChildTwo.js
function ChildTwo() {
return (
<div><ChildThree /></div>
);
// ChildThree.js (this is where you consume a context to use/consume the data passed
down from MyParent component)
function ChildThree() {
return (
<div>
<MyContext.Consumer>
{function (value) {
return <div>{value}</div>;
}}
</MyContext.Consumer>
</div>
);
▪ Problems with performance: The Context API uses a comparison algorithm that
compares the value of its current state to any update it receives, and whenever a
change occurs, the Context API broadcasts this change to every component
consuming its provider, which in turn results in a re-render of these components.
This would seem trivial at first glance, but when we rely heavily on Context for
basic state management, we needlessly push all of our states into a context
provider. As you would expect, this is not very performant when many components
depend on this Context Provider, as they will re-render whenever there is an update
to the state regardless of whether the change concerns or affects them or not.
● Why do we then need the useContext() hook? We have seen above how we can use
Context API to avoid prop drilling and send data from parent components to children. To
make data consuming easy, React 16.8 introduced the useContext() hook. When we use
the useContext() hook, we do not need the render props technique (we used under Context
API). Rather, we can pass the entire context object (which we created in the parent
component) into our React.useContext() hook on top of our consuming child component.
In short, the useContext() hook was introduced to simplify the consuming part of context
API.
● Steps to use useContext(): Step 1, Step 2 and Step 3 we used for Context APU above
remain the same when using the useContext() hook. However, the consuming step is
differnent here.
▪ Step 1: create a context using the createContext() method from React and save it
on a variable. Make sure to export the context you created because in most cases
your component will be in another file. Example:
▪ Step 2: Take your created context and wrap the context provider around the child
components you want to pass down data to. Note: The context you created above,
the “MyContext” , is an object that has two properties, namely Provider and
Consumer. Both Provider and Consumer are components. So, go ahead and wrap
your Provider component around your child component like below.
▪ Example:
● <MyContext.Provider>
● <ChildComponent/>
● </MyContext.Provider>
▪ Step 3: Put the value you want to pass to any child components on your context
provider using the value prop.
▪ Example:
● <ChildOne />
● </MyContext.Provider>
▪ Step 4: Go to the child component you finally want to consume your data/value.
Import the useContext() hook from React. Also, import the context your created in
your parent component.
▪ Example:
▪ Step 5: In the above child component, on top of the component, call the useContext
() hook and pass the entire context object as its argument and put this value on a
variable.
▪ Example:
▪ Step 6: In the above child component, consume/read the value of the context in
your child component. To do this, make your component return the above (
“myValue” ).
▪ Example:
// App.js
import React from "react";
function App() {
return (
<div>
<MyParent />
</div>
);
// MyParent.js
function MyParent() {
return (
<div>
<MyContext.Provider value="Alem">
<ChildOne />
</MyContext.Provider>
</div>
);
// ChildOne.js
import React from "react";
function ChildOne() {
return (
<div><ChildTwo /></div>
);
// ChildTwo.js
function ChildTwo() {
return (
<div>
<ChildThree />
</div>
);
// ChildThree.js
function ChildThree() {