100% found this document useful (4 votes)
17K views

React Hooks Cheat Sheet: Usestate Usereducer

This document provides a cheat sheet on React Hooks, including useState, useEffect, and useReducer. It discusses: 1. How to declare and update local state with useState, including lazy initialization of state. 2. How to trigger side effects on mount, update, and unmount with useEffect. 3. Common causes of infinite update loops with state updates, such as updating state values that depend on each other, and fully replacing object state. It provides solutions such as tracking the source of the update. 4. How to manage state with useReducer, including initializing state, dispatching actions, and handling different action types. 5. Examples of custom hooks

Uploaded by

Roberto Scioni
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (4 votes)
17K views

React Hooks Cheat Sheet: Usestate Usereducer

This document provides a cheat sheet on React Hooks, including useState, useEffect, and useReducer. It discusses: 1. How to declare and update local state with useState, including lazy initialization of state. 2. How to trigger side effects on mount, update, and unmount with useEffect. 3. Common causes of infinite update loops with state updates, such as updating state values that depend on each other, and fully replacing object state. It provides solutions such as tracking the source of the update. 4. How to manage state with useReducer, including initializing state, dispatching actions, and handling different action types. 5. Examples of custom hooks

Uploaded by

Roberto Scioni
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1

React Hooks cheat sheet

Cause 2: states updating each other


Local state: useState Reducers to manage state with useReducer
useEffect(() => {
Declare the state setSecond(first * 3) Initialize a local state:
}, [first])
const [name, setName] = useState('initial value') const initialState = {
useEffect(() => { value: 0,
setFirst(second / 2) }
Update the state }, [second])

setName('new value') // Triggers an infinite update loop :(


Create the reducer:
// or <button onClick={() => setFirst(5)}/>
setName((value) => 'new ' + value) const reducer = (state, action) => {
switch (action.type) {
Solution 2: store in a state which value was updated by the user: case 'increment':
Lazy-initialize the state
// Must create a new state, not modify the current one!
const [updating, setUpdating] = useState(null) return { ...state, value: state.value + 1 }
const [value, setValue] = useState(
case 'set_to':
// Evaluated only at first rendering useEffect(() => { return { ...state, value: action.value }
() => computeValue() if (isUpdating === 'first') setSecond(first * 3) default:
) }, [first, isUpdating]) throw new Error('Unhandled action')
}
useEffect(() => { }
Side effects: useEffect if (isUpdating === 'second') setFirst(second / 2)
}, [second, isUpdating])
Create a local state using useReducer :
Trigger side effects when the component is mounted
// No infinite update loop :)
<button onClick={() => { const [state, dispatch] = useReducer(reducer, initialState)
useEffect(() => {
setUpdating('first')
// HTTP request, setTimeout, etc. <span>{state.value}</span>
setFirst(5)
doSomething()
} />
}, [])
Dispatch actions to update the state:
Cause 3: using an object state
Trigger side effects each time a prop or state is updated <button onClick={() => {
const [object, setObject] = useState({ value: 'aaa', changes: 0 }) dispatch({ type: 'increment' })
useEffect(() => { }} />
doSomethingWith(value) <button onClick={() => {
useEffect(() => {
}, [value]) dispatch({ type: 'set_to', value: 42 })
setObject({ ...object, changes: object.changes + 1 })
}, [object]) }} />
Clean something when the component is unmounted:
<button onClick={() => {
useEffect(() => { // Will trigger an infinit loop :( Examples of hooks to access the browser API
let timeout = setTimeout(doSomething, 5000) setObject({ ...object, value: 'bbb' })
return () => clearTimeout(timeout) }} /> Persist a state in the local storage
}, [])
Solution 3: watch only some of the object’s attributes const usePersistedState = (key, initialValue) => {
const [value, setValue] = useState(initialValue)
Rules when using hooks const [object, setObject] = useState({ value: 'aaa', changes: 0 })
useEffect(() => {
Must only be used in function components useEffect(() => { const existingValue = localStorage.getItem(key)
setObject({ ...object, changes: object.changes + 1 }) if (existingValue !== null) {
Only at component top-level (not in if ) }, [object.value]) // watch only the `value` attribute setValue(existingValue)
}
No return before any hook <button onClick={() => { }, [key])
// No infinit loop :)
setObject({ ...object, value: 'bbb' }) const setAndPersistValue = (newValue) => {
Custom hooks }} /> setValue(newValue)
localStorage.setItem(key, newValue)
Must start with use }

Used to extract common behavior of components, like async requests: Memoize a value with useMemo
return [value, setAndPersistValue]
}
const useApiResult = (param) => { const value = useMemo(() => {
const [result, setResult] = useState(null) // Will be evalutated only when param1 or param2 change // Usage
return expensiveOperation(param1, param2) const [name, setName] = usePersistedState('name', 'John Doe')
useEffect(() => { }, [param1, param2])
fetch('http://your.api?param=' + param)
Get an element’s size
.then((res) => res.json())
.then((result) => setResult(result))
}, [])
Memoize a callback with useCallback const useElementSize = (elementRef) => {
const [width, setWidth] = useState(undefined)
return { result } // Will return a new function only when param1 or param2 change const [height, setHeight] = useState(undefined)
} const handleClick = useCallback(() => {
// To use it in a component: doSomethingWith(param1, param2) useEffect(() => {
const { result } = useApiResult('some-param') }, [param1, param2]) const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
if (entry.contentRect) {
Memoize callback for a dynamic list of elemments: setWidth(entry.contentRect.width)
Getting the current state in async code setHeight(entry.contentRect.height)
// The same function for all the buttons created dynamically }
Problem: in a useEffect , you want to get the value of the current state to const handleClick = useCallback((event) => { }
const button = event.target })
update it (e.g. increment a number)
const value = button.getAttribute('data-value') resizeObserver.observe(elementRef.current)
doSomethingWith(value)
const [val, setVal] = useState(0)
}, []) return () => {
useEffect(() => {
setInterval(() => { resizeObserver.disconnect()
<ul> }
setVal(val + 1)
{objects.map((obj) => ( }, [elementRef])
}, 1000)
<li key={obj.id}>
}, [])
<button data-value={obj.value} onClick={handleClick}> return [width, height]
console.log(val) // always logs 0 :(
{obj.value} }
</button>
Solution 1: use the other syntax of setValue : </li> // Usage
))} const div = useRef()
const [val, setVal] = useState(0) </ul> const [width, height] = useElementSize(div)
useEffect(() => { <div style={{ resize: 'both' }} ref={div} />
setInterval(() => {
// val always contain the current value Contexts & provider/consumer with useContext Get the user’s geolocation
setVal((val) => val + 1)
}, 1000)
}, [])
Create the context: const useGeolocation = () => {
console.log(val) // logs 0, 1, 2, 3... :) const [status, setStatus] = useState('pending')
const themeContext = createContext() const [latitude, setLatitude] = useState(undefined)
const [longitude, setLongitude] = useState(undefined)
Solution 2: use a ref containing the current value:
Create a specific provider for the context:
useEffect(() => {
const [val, setVal] = useState(0) navigator.geolocation.getCurrentPosition(
const ThemeProvider = ({ children, initialTheme = 'light' }) => {
(res) => {
const valRef = useRef(val) const [theme, setTheme] = useState(initialTheme)
setStatus('success')
useEffect(() => (valRef.current = val), [val]) return (
setLatitude(res.coords.latitude)
<themeContext.Provider value={[theme, setTheme]}>
setLongitude(res.coords.longitude)
useEffect(() => { {children}
},
setInterval(() => { </themeContext.Provider>
(err) => {
// valRef.current contains the current value )
console.log(err)
setVal(valRef.current + 1) }
setStatus('error')
}, 1000) }
}, []) // Usage
)
console.log(val) // logs 0, 1, 2, 3... :) ;<ThemeProvider initialTheme="dark">
}, [])
<Label>Hello</Label>
</ThemeProvider>
return { status, latitude, longitude }
Solving infinite loops with useEffect }
Create a custom hook to consume the context:
Cause 1: no dependencies array: // Usage
const useTheme = () => { const { status, latitude, longitude } = useGeolocation()
const [theme, setTheme] = useContext(themeContext)
useEffect(() => {
// Add here additional logic if necessary...
asyncFunction().then((res) => setValue(res))
return [theme, setTheme]
})
} Struggling with hooks, or want to be more comfortable with them?
Solution: always pass an array as second parameter to useEffect : // Usage
Learn how to use them and solve the problems they cause:
const [theme, setTheme] = useTheme()
useEffect(() => {
// ...  
}, [])

Created by@scastiel

You might also like