26
loading...
This website collects cookies to deliver better user experience
Discounts Applied
for my Hashnode readers only!useState({})
hook doesn't merge state automatically.setState()
and setEffect()
are the two hooks you want to learn first.useContext()
, useRef()
and useReducer()
setState()
imitate class-based state in function components.setEffect()
imitate multiple lifecycle methods with just 1 function.useContext()
with </Context.Provider>
and </Context.Consumer>
useRef()
one use case is to grab some instances of elements from DOM.useReducer()
to be used with a reducer function.useImperativeHandle()
while useRef()
gives instance of component ref is attached to this is similar. But it also gives you control over return value. It will allow you to replace native events like blur, focus, mousemove etc. with your own functions to run side effects on or rewrite their behavior.useLayoutEffect()
similar to useEffect()
but for post-update side effects. Occurs after all DOM mutations. This avoids jumpy behavior when dealing with things like calculating properties that deal with element's dimensions like top, left, width and height.useDebugValue()
Called only when React DevTools are open and related hook is inspected. In some cases this can prevent low performance when you need to narrow down on a specific value or property.useState
hook provides a way for your functional component to create and access state data without having to specify it in class constructors.useState()
first you have to import it from react:import { useState } from "react";
function App(props) {
// This is the state hook - add a state variable number:
const [number, setNumber] = useState(2);
return (<div>Number: {number}</div>);
}
useState
takes a default value.Number: 2
useState()
hook you are "hooking" into React's state functionality without having to define state in a class constructor. However the syntax is much simpler and that makes your code look better and easier to read and write.this.setState({})
method to set state data which will trigger automatic component update.this.setState({ number: 2 })
{ number: 2 }
with any other data present in the class-based component's state object. useState
hook does not! useState
will replace the entire value with the new data. We'll take a look at this later on.useState()
hook requires to provide value AND setter function. In first example above value name wasnumber and setNumber
is its setter.this.setState({ number: 1000 })
method from class-based components. Instead you will use setNumber
:function App(props) {
// Add a state variable number:
const [number, setNumber] = useState(0);
// Event function for increasing number by 1
const inc = () => {
// use the setNumber setter function
setNumber(number + 1)
}
return (<button onClick = {inc}>{number}</button>);
}
1
useState()
supports all common JavaScript data structures.useState()
for a while you'll find yourself doing stuff like:const [number, setNumber] = useState(0);
const [loading, setLoading] = useState(true);
const [title, setTitle] = useState("Title");
const [movies, setMovies] = useState(["Alien", "King Kong"]);
const [data, setData] = useState({key: "skeleton"});
useState()
for each separate value?function Cat(props) {
const [state, setState] = useState({
name: "Luna",
age: 2,
legs: 4,
state: "Sleeping",
})
return (<div>
Render cat {state.name} with {state.legs} legs.
</div>)
}
setState({ legs: 5 })
useState
with objects or arrays.useState()
hook and the older way of updating state using class-based this.setState()
function when it comes to updating more complex data structures like arrays and objects.[]
or Object {}
.useState()
hook with objects {}
or arrays []
useState()
hooks with an object:function Component() {
let [state, setState] = useState({
name: "Luna",
age: 2,
legs: 4,
state: "Sleeping"})
}
setState()
function to change name from Luna to Felixconst changeName = () => {
setState({ name: "Felix" })
}
return <>
<h2>Hello Hooks</h2>
<button onClick = {changeName}>Change Name To "Felix"</button>
<div>
<div>Name: {state.name}</div>
<div>{state.legs} legs</div>
<div>{state.age} years old</div>
<div>{state.state}</div>
</div>
</>
setState({ name:"Felix" })
setter function replaces the entire object with whatever we pass to it without merging it.<= 16.8
React.this.setState({})
method you know that it will automatically merge whatever you pass to it with existing state data. However with this useState({})
hook this is not the case. You must merge it yourself before passing the new value to the setter function.this.setState({ name: "Felix" })
setState
hook does not merge state automatically{}
and []
data structures.…
operator (actually it's a notation not an operator, but it's tempting to call it that) was added to JavaScript a while ago in EcmaScript 6:let cat1 = { name: "Felix" }
let cat2 = { legs: 4 }
let merged = {...cat1, ...cat2 }
console.log( merged )
>>>
{name: "Felix", legs: 4}
let cat1 = ["Luna"]
let cat2 = ["Felix"]
let merged = [...cat1, ...cat2]
console.log( merged )
>>>
["Luna", "Felix"]
changeName
function to support …
rest/spread notation.{}
const changeName = () => {
setState({...state, name: "Felix"})
}
array[]
you would do something like:const changeArrayValue = () => {
setState([...state, "Felix"])
}
useEffect()
tells React to do something after rendering the component.useEffect()
hook can imitate multiple lifecycle events in one function!array[]
(has its own special meaning) or a list of state object dependencies [state1, state2, ...N]
)useEffect
is that scheduled effects won't block your browser like lifecycle components would. This makes your UI even more fluid. And this is another good reason to start using hooks instead of class-based design.useEffect()
import it:import { useEffect } from "react";
function App() {
let [val, setVal] = useState(0)
let [num, setNum] = useState(0)
useEffect(() => {
// something happens here on a lifecycle event
})
return (<div>{val}</div>)
}
useEffect()
acts as a combination of 3 lifecycle methods: componentDidMount
, componentDidUpdate
and componentWillUnmount
.componentDidMount
is triggered when your component is mounted.componentDidUpdate
is triggered right after component is rendered.componentWillUnmount
is invoked when component is about to be removed from the DOM. Usually this is where you do data clean up.setState
's behavior is defined by what you do with the second argument which is a dependencies[]
array. By default its undefineduseEffects(effect, dependencies[])
dependencies[]
is optional for simple use case. But its key to understanding and taking full advantage of useEffects
dependencies[]
you can narrow down not only on which lifecycle methods you want useEffect
to be triggered for but also choose which particular state objects you want this effect to trigger on for future updates.dependencies[]
array:dependencies[]
completely your effect on this component will update in at least two default cases:useEffect()
example.dependencies[]
array exists but it is empty []
.[]
array effect is executed only once for the first timefunction App() {
let [val, setVal] = useState(0)
useEffect(() => {
// same as componentDidUpdate -- but fires only once!
}, [])
return (<div>{val}</div>)
}
[]
as second argument of useEffect
function App() {
let [val, setVal] = useState(0)
let [num, setNum] = useState(100)
let [txt, setTxt] = useState('text')
useEffect(() => {
// same as componentDidUpdate
// AND fires in the future only for val changes
// nothing happens if either num or txt change
}, [val])
return (<div>{val}</div>)
}
val
, num
and txt
state variables.[val]
in dependencies[]
array.useEffect()
will trigger on mount and whenever val is updated.dependencies[]
is missing it's like executing useEffect
whenever any of the state variables defined with useState
change.[val]
then useEffect
excludes all other other state variables and will be executed only when val
changes. If any other state objects change useEffect
will not be executed for them.dependencies[]
array as a filter.useEffect(() => {
// also executes in the future for val and num
// but not for txt
}, [val, num])
txt
changes this effect will not execute.const action = {
learning: 1,
working: 2,
sleeping: 3
}
const ActionContext = createContext(action)
function App(props) {
return (
<ActionContext.Provider value={action.sleeping}>
<ActionPicture />
</ActionContext.Provider>
)
}
ActionContext
is the provider that provides the value action.function ActionPicture() {
const action = useContext(ActionContext);
return <div>{ action }</div>
}
// consumer component
function ActionPicture() {
return <ActionContext.Consumer>{
({ action }) => <div>{ action }</div>
}</ActionContext.Consumer>
}
import { useRef } from 'react';
.current
property on the ref
.current
can point to a regular variable or link to a DOM object which depends on how you initialized your ref
and where it's used.)useRef(initialValue)
to create persisting mutable values.useRef(initialValue)
if you need to get instance of DOM element.// value is changed but nothing happens when button is clicked
function App() {
const count = useRef(0)
return (<button onClick={() => count.current++}>
{count.current}
</button>);
}
count.current
value will change but on the screen it remains at 0
even if button is clicked multiple times. Changing it won't trigger a redraw.// use useRef if you want to grab element from DOM
function App() {
const butt = useRef(null)
const clickIt = () => butt.current.click()
return (<button ref={butt}></button>)
}
.click()
method.butt.current
is the link to the button's element in the DOM.import { useRef, useEffect } from 'react';
function SearchQueryInput() {
const queryRef = useRef()
// Note: queryRef is still 'undefined' here
useEffect(() => {
// But here queryRef becomes a
// valid HTMLInputElement
queryRef.current.focus()
})
return (
<input
ref = {queryRef}
type = "text"
/>
);
}
First we create our queryRef
with useRef()
, this will hold an object reference to the input element (which will be pointed to by queryRef.current
property not queryRef
itself.)
When useEffect
executes on this component (which will happen soon after first render) we call .focus()
method on queryRef.current object
. This automatically gives our search query input field typing focus.
The return value which is just the <input>
element is linked to queryRef
object via the ref attribute. It's assigned to {queryRef}
which is the variable name we assigned to result returned from useRef()
hook.
Note that initially queryRef
is still undefined soon as its created. It becomes available only in useEffect
after component is mounted.
import { useRef } from 'react';
const memoized = useMemo(() => sum(a, b), [a, b]);
useMemo()
[a, b]
is the dependency array. It should contain all values referenced inside the function. This is what potentially improves performance.useMemo
will run during component rendering. Avoid doing anything here that would trigger a re-render (like change state.) Side effects should go in useEffect
hook.useMemo
. React doesn't always guarantee its execution. But it does provide additional optimizations when it makes the most sense.useMemo(myFunction, [arg])
this won't work.useMemo(() => myFunction(), [arg])
useMemo()
we can return memoized values and avoid re-rendering. This works as long as the function's arguments have not changed.useMemo
should be used to memoize entire components or how exactly to do it. So (if that's even possible) I'll work on this and included it in this section later.React.memo()
method to achieve that. (Even though it's not really part of React hooks.)// Song.js
export function Song({ title, singer, year }) {
return(
<div>
<div>Song title: {title}</div>
<div>Singer: {band}</div>
<div>Release year: {year}</div>
</div>
)
}
// Export Song as memoized component
export const MemoizedSong = React.memo(Song);
<MemoizedSong
title="Lose Yourself"
singer="Eminem"
year="2002"
/>
React.memo()
does a shallow comparison. This means only first-level properties will be compared without checking full hierarchy of objects.React.memo(Component, [ areEqual(prevProps, nextProps) ]);
areEqual
function returns true if previous and next props are the same.<Component>
is functional and given the same props so it always renders the same output.<Component>
is always (or often) rendered with the same props.<Component>
contains a decent amount of UI elements.React.memo()
can cause performance issues if not correctly implemented.import { useCallback } from 'react';
useCallback
hook is used with callback functions. This basically memoizes callback functions making them efficient.function ClickMe() {
const doClick = useCallback(() => {
// handle click
}, [])
return (<button onClick = {doClick}>Click Me</button>)
}
useMemo
hook useCallback
will memoize callbacks.ClickMe
component below notice doClick
function:function ClickMe() {
const doClick = () => {
console.log('Button Clicked!')
}
}
doClick
function is created. Inline functions are inexpensive so a new object is created.React.memo()
. This function accepts props.useEffect(effect, [callback])
function ClickMe() {
const doClick = useCallback(() => {
// handle click
}, [])
}
doClick
will always refer to same callback function. This can improve performance if used strategically in certain places in your app.useCallback
is when rendering long lists of components. Instead of having React assign a new callback function to each component the same function can be used.import { useReducer } from 'react';
function App() {
const [state] = useReducer()
return (<>Count: {state}</>)
}
function reducer(state, action) {
switch (action.type) {
case 'add':
return state + 1;
case 'subtract':
return state - 1;
case 'double':
return state * 2;
default:
throw new Error();
}
}
// returns an array of 2 values: state and dispatch
function App() {
// 2nd arg = initial state
const [state] = useReducer(reducer, 10)
return (<>Count: {state}</>)
}
return(<>
Count: {state}
<button onClick={() => dispatch({type: 'add'})}> + </button>
<button onClick={() => dispatch({type: 'subtract'})}> - </button>
<button onClick={() => dispatch({type: 'double'})}> X2 </button>
</>)
useReducer
takes reducer function and initial value (10 in this case).useReducer
is usually used together with dispatch function.dispatch
function will often define action type as one of its arguments.reducer()
here.)useState
and useEffect
enough?const useCustomHook = value => {
useEffect(() => {
console.log(`Do something, val = ${value}`);
}, []);
)
}
function Something() {
const [count, setCount] = useState(0);
const inc = () => setCount(count + 1);
const value = `The count is ${count}`;
// Implement your hook
useCustomHook( value )
return(<div>
<h1>{count}</h1>
<button onClick = {inc}>Increase by 1</button>
</div>);
}
useEffect
hook, for example, inherits same functionality of lifecycle methods. But your code is cleaner. And it makes it easier to write same efficient code.Discounts Applied
for my Hashnode readers only!