react-pro-dev-1-hooks
React Pro Dev - 1
React Hooks cheat sheet: Best practices with examples
cheatsheet tool: https://react-hooks-cheatsheet.com/uselayoutEffect
https://blog.logrocket.com/react-hooks-cheat-sheet-unlock-solutions-to-common-problems-af4caf699e70/
const UpdateStateVar = () => {
const [age, setAge] = useState(19)
const handleClick = () => setAge(age + 1)
return (
<div>
Today I am {age} Years of Age
<div>
<button onClick={handleClick}>Get older! </button>
</div>
</div>
)
}
If you find that useState/setState are not updating immediately, the answer is simple: they’re just queues.
React Hooks cheat sheet: Best practices with examples
https://blog.logrocket.com/react-hooks-cheat-sheet-unlock-solutions-to-common-problems-af4caf699e70/
const UpdateStateVar = () => {
const [age, setAge] = useState(19)
const handleClick = () => setAge(age + 1)
return (
<div>
Today I am {age} Years of Age
<div>
<button onClick={handleClick}>Get older! </button>
</div>
</div>
)
}
If you find that useState/setState are not updating immediately, the answer is simple: they’re just queues.
As opposed to just passing an initial state value, state could also be initialized from a function, as shown below:
const StateFromFn = () => {
const [token] = useState(() => {
let token = window.localStorage.getItem("my-token");
return token || "default#-token#"
})
return <div>Token is {token}</div>
}
const StateFromFn = () => {
const [token] = useState(() => {
let token = window.localStorage.getItem("my-token");
return token || "default#-token#"
})
return <div>Token is {token}</div>
}
Both forms of updating state are valid:
const [value, updateValue] = useState(0)
// both forms of invoking "updateValue" below are valid 👇
updateValue(1);
updateValue(previousValue => previousValue + 1);
Both forms of updating state are valid:
const [value, updateValue] = useState(0)
// both forms of invoking "updateValue" below are valid 👇
updateValue(1);
updateValue(previousValue => previousValue + 1);
useEffect() Beta docs
https://beta.reactjs.org/reference/react/useEffect
useEffect is a Hook, so you can only call it at the top level of your component or your own Hooks. You can’t call it inside loops or conditions. If you need that, extract a new component and move the state into it.
If you’re not trying to synchronize with some external system, you probably don’t need an Effect.
When Strict Mode is on, React will run one extra development-only setup+cleanup cycle before the first real setup. This is a stress-test that ensures that your cleanup logic “mirrors” your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, you need to implement the cleanup function.
If some of your dependencies are objects or functions defined inside the component, there is a risk that they will cause the Effect to re-run more often than needed. To fix this, remove unnecessary object and function dependencies. You can also extract state updates and non-reactive logic outside of your Effect.
If your Effect wasn’t caused by an interaction (like a click), React will let the browser paint the updated screen first before running your Effect. If your Effect is doing something visual (for example, positioning a tooltip), and the delay is noticeable (for example, it flickers), you need to replace useEffect with useLayoutEffect.
Even if your Effect was caused by an interaction (like a click), the browser may repaint the screen before processing the state updates inside your Effect. Usually, that’s what you want. However, if you must block the browser from repainting the screen, you need to replace useEffect with useLayoutEffect.
Effects only run on the client. They don’t run during server rendering.
useEffect() Beta docs
https://beta.reactjs.org/reference/react/useEffect
useEffect is a Hook, so you can only call it at the top level of your component or your own Hooks. You can’t call it inside loops or conditions. If you need that, extract a new component and move the state into it.
If you’re not trying to synchronize with some external system, you probably don’t need an Effect.
When Strict Mode is on, React will run one extra development-only setup+cleanup cycle before the first real setup. This is a stress-test that ensures that your cleanup logic “mirrors” your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, you need to implement the cleanup function.
If some of your dependencies are objects or functions defined inside the component, there is a risk that they will cause the Effect to re-run more often than needed. To fix this, remove unnecessary object and function dependencies. You can also extract state updates and non-reactive logic outside of your Effect.
If your Effect wasn’t caused by an interaction (like a click), React will let the browser paint the updated screen first before running your Effect. If your Effect is doing something visual (for example, positioning a tooltip), and the delay is noticeable (for example, it flickers), you need to replace useEffect with useLayoutEffect.
Even if your Effect was caused by an interaction (like a click), the browser may repaint the screen before processing the state updates inside your Effect. Usually, that’s what you want. However, if you must block the browser from repainting the screen, you need to replace useEffect with useLayoutEffect.
Effects only run on the client. They don’t run during server rendering.
An Effect lets you keep your component synchronized with some external system (like a chat service). Here, external system means any piece of code that’s not controlled by React, such as:
- A timer managed with setInterval() and clearInterval().
- An event subscription using window.addEventListener() and window.removeEventListener().
- A third-party animation library with an API like animation.start() and animation.reset().
- If you’re not connecting to any external system, you probably don’t need an Effect.
An Effect lets you keep your component synchronized with some external system (like a chat service). Here, external system means any piece of code that’s not controlled by React, such as:
- A timer managed with setInterval() and clearInterval().
- An event subscription using window.addEventListener() and window.removeEventListener().
- A third-party animation library with an API like animation.start() and animation.reset().
- If you’re not connecting to any external system, you probably don’t need an Effect.
useEffect()
https://blog.logrocket.com/react-hooks-cheat-sheet-unlock-solutions-to-common-problems-af4caf699e70/
Add and remove an event listener
const EffectCleanup = () => {
useEffect(() => {
const clicked = () => console.log('window clicked')
window.addEventListener('click', clicked)
// return a clean-up function
return () => {
window.removeEventListener('click', clicked)
}
}, [])
return <div>
When you click the window you'll
find a message logged to the console
</div>
}
useEffect()
https://blog.logrocket.com/react-hooks-cheat-sheet-unlock-solutions-to-common-problems-af4caf699e70/
Add and remove an event listener
const EffectCleanup = () => {
useEffect(() => {
const clicked = () => console.log('window clicked')
window.addEventListener('click', clicked)
// return a clean-up function
return () => {
window.removeEventListener('click', clicked)
}
}, [])
return <div>
When you click the window you'll
find a message logged to the console
</div>
}
Modal
import { useEffect, useRef } from 'react';
export default function ModalDialog({ isOpen, children }) {
const ref = useRef();
useEffect(() => {
if (!isOpen) {
return;
}
const dialog = ref.current;
dialog.showModal();
return () => {
dialog.close();
};
}, [isOpen]);
return <dialog ref={ref}>{children}</dialog>;
}
Modal
import { useEffect, useRef } from 'react';
export default function ModalDialog({ isOpen, children }) {
const ref = useRef();
useEffect(() => {
if (!isOpen) {
return;
}
const dialog = ref.current;
dialog.showModal();
return () => {
dialog.close();
};
}, [isOpen]);
return <dialog ref={ref}>{children}</dialog>;
}
With a an empty array dependency, the effect function will be called only on mount.
Without an array dependency, the effect function will be run after every single render.
With a dependency in the array, the useEffetc will be run only when that variable changes.
With a an empty array dependency, the effect function will be called only on mount.
Without an array dependency, the effect function will be run after every single render.
With a dependency in the array, the useEffetc will be run only when that variable changes.
useChatRoom custom Hook “hides” the logic of your Effect
function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
}
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...
useChatRoom custom Hook “hides” the logic of your Effect
useChatRoom custom Hook “hides” the logic of your Effect
function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
}
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...
useContext()
useContext saves you the stress of having to rely on a Context consumer. React Context has a simpler API when compared to MyContext.Consumer and the render props API it exposes.
Context is React’s way of handling shared data between multiple components.
Difference:
// example Context object
const ThemeContext = React.createContext("dark");
// usage with context Consumer
function Button() {
return <ThemeContext.Consumer>
{theme => <button className={theme}> Amazing button </button>}
</ThemeContext.Consumer>
}
// usage with useContext hook
import {useContext} from 'react';
function ButtonHooks() {
const theme = useContext(ThemeContext)
return <button className={theme}>Amazing button</button>
}
useContext()
useContext saves you the stress of having to rely on a Context consumer. React Context has a simpler API when compared to MyContext.Consumer and the render props API it exposes.
Context is React’s way of handling shared data between multiple components.
Difference:
// example Context object
const ThemeContext = React.createContext("dark");
// usage with context Consumer
function Button() {
return <ThemeContext.Consumer>
{theme => <button className={theme}> Amazing button </button>}
</ThemeContext.Consumer>
}
// usage with useContext hook
import {useContext} from 'react';
function ButtonHooks() {
const theme = useContext(ThemeContext)
return <button className={theme}>Amazing button</button>
}
Simple useContext() example
import { useContext } from 'react';
// Create the context
const MyContext = React.createContext();
function MyComponent() {
// Consume the value from the context
const value = useContext(MyContext);
return (
<div>
{value}
</div>
);
}
function App() {
return (
<MyContext.Provider value="Hello World">
<MyComponent />
</MyContext.Provider>
);
}
Simple useContext() example
import { useContext } from 'react';
// Create the context
const MyContext = React.createContext();
function MyComponent() {
// Consume the value from the context
const value = useContext(MyContext);
return (
<div>
{value}
</div>
);
}
function App() {
return (
<MyContext.Provider value="Hello World">
<MyComponent />
</MyContext.Provider>
);
}
useLayoutEffect()
import { useLayoutEffect } from 'react';
function MyComponent() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const element = document.getElementById("my-element");
setWidth(element.clientWidth);
}, [])
return (
<div id="my-element">
Width: {width}
</div>
);
}
There are cases where you may not want the behavior useEffect provides, though; for example, if you need to make a visual change to the DOM as a side effect, useEffect won’t be the best choice.
In this example, MyComponent uses the useLayoutEffect hook to measure the width of the div element with the id "my-element" and update the width state variable with the measured value.
The function passed to useLayoutEffect will run after the browser has painted the updated layout, which allows you to synchronously read layout information and update the component based on that information. This differs from useEffect which runs after the browser has painted the updated layout and the updated state, which means that it might run after the updated layout is not yet visible.
It's important to note that useLayoutEffect should be used sparingly and only when you need to synchronously read layout information because it will block the browser from updating the layout until the effect has been executed.
useLayoutEffect()
import { useLayoutEffect } from 'react';
function MyComponent() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const element = document.getElementById("my-element");
setWidth(element.clientWidth);
}, [])
return (
<div id="my-element">
Width: {width}
</div>
);
}
There are cases where you may not want the behavior useEffect provides, though; for example, if you need to make a visual change to the DOM as a side effect, useEffect won’t be the best choice.
In this example, MyComponent uses the useLayoutEffect hook to measure the width of the div element with the id "my-element" and update the width state variable with the measured value.
The function passed to useLayoutEffect will run after the browser has painted the updated layout, which allows you to synchronously read layout information and update the component based on that information. This differs from useEffect which runs after the browser has painted the updated layout and the updated state, which means that it might run after the updated layout is not yet visible.
It's important to note that useLayoutEffect should be used sparingly and only when you need to synchronously read layout information because it will block the browser from updating the layout until the effect has been executed.
useReducer
- useReducer is a hook in React that allows you to manage state and handle state transitions in a predictable way.
- It is similar to useState, but it is more powerful and flexible for handling complex state changes and logic.
- useReducer takes two arguments: the reducer function and the initial state. The reducer function takes the current state and an action, and returns the next state.
- useReducer returns an array with two elements: the current state and a dispatch function, which is used to update the state by calling the reducer function with an action.
- The reducer function should be a pure function, which means that it should not have any side effects and should only depend on its inputs.
- By using the useReducer hook, you can implement a centralized state management pattern, similar to using a store in a Redux application.
- useReducer is a good choice when you need more control over your state updates, such as handling complex logic, handling async actions, and having multiple state values that are dependent on each other.
useReducer
- useReducer is a hook in React that allows you to manage state and handle state transitions in a predictable way.
- It is similar to useState, but it is more powerful and flexible for handling complex state changes and logic.
- useReducer takes two arguments: the reducer function and the initial state. The reducer function takes the current state and an action, and returns the next state.
- useReducer returns an array with two elements: the current state and a dispatch function, which is used to update the state by calling the reducer function with an action.
- The reducer function should be a pure function, which means that it should not have any side effects and should only depend on its inputs.
- By using the useReducer hook, you can implement a centralized state management pattern, similar to using a store in a Redux application.
- useReducer is a good choice when you need more control over your state updates, such as handling complex logic, handling async actions, and having multiple state values that are dependent on each other.
Import the useReducer hook from the react library.
import { useReducer } from 'react';
Create the reducer function, which takes the current state and an action, and returns the next state. The reducer function should be a pure function and should not have any side effects.
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
- Declare the initial state in the component.
const initialState = { count: 0 };
- Use the useReducer hook in the component, passing the reducer function and the initial state as arguments.
const [state, dispatch] = useReducer(reducer, initialState);
Import the useReducer hook from the react library.
import { useReducer } from 'react';
Create the reducer function, which takes the current state and an action, and returns the next state. The reducer function should be a pure function and should not have any side effects.
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
- Declare the initial state in the component.
const initialState = { count: 0 };
- Use the useReducer hook in the component, passing the reducer function and the initial state as arguments.
const [state, dispatch] = useReducer(reducer, initialState);
- Use the returned state and dispatch function in the component. You can use the state to render the component and the dispatch function to update the state by calling it with an action.
return (
<div>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
{state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</div>
);
- (Optional) You can also pass a second argument to useReducer to pass some initial action to be dispatched to the reducer function, this could be useful in some cases like when you need to set some initial value or call some API.
const [state, dispatch] = useReducer(reducer, initialState, (initialAction)=>dispatch(initialAction));
- Use the returned state and dispatch function in the component. You can use the state to render the component and the dispatch function to update the state by calling it with an action.
return (
<div>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
{state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</div>
);
- (Optional) You can also pass a second argument to useReducer to pass some initial action to be dispatched to the reducer function, this could be useful in some cases like when you need to set some initial value or call some API.
const [state, dispatch] = useReducer(reducer, initialState, (initialAction)=>dispatch(initialAction));
useReducer example
https://react-hooks-cheatsheet.com/usereducer
const initialState = { width: 15 }
const reducer = (state, action) => {
switch (action) {
case 'plus':
return { width: state.width + 15 }
case 'minus':
return { width: Math.max(state.width - 15, 2) }
default:
throw new Error("what's going on?" )
}
}
const Bar = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return <>
<div style={{ background: 'teal', height: '30px', width: state.width }}></div>
<div style={{marginTop: '3rem'}}>
<button onClick={() => dispatch('plus')}>Increase bar size</button>
<button onClick={() => dispatch('minus')}>Decrease bar size</button>
</div>
</>
}
render(Bar)
useCallback()
The useCallback hook in React is used to ensure that a specific callback function only changes if one of its dependencies has changed. This is particularly useful when the callback function is passed as a prop to a child component and the child component is re-rendered frequently.
const doSomething = useCallback(() => {
return someValue
}, [someValue])
The useCallback hook can be used in the following situations:
When you have a child component that is re-rendered frequently, and the callback function passed to it as a prop is defined in the parent component.
When you are passing a callback function as a prop to a component that is located several levels down in the component tree and you want to avoid unnecessary re-renders.
When you have a performance-sensitive component that re-renders frequently and you want to avoid unnecessary re-creating callback functions.
When you are using a callback function that is used in an effect hook, it will ensure that the effect only updates if the dependencies of the callback have changed.
By using the useCallback hook, you can ensure that the callback function is only re-created if one of its dependencies has changed, which can improve the performance of your application by avoiding unnecessary re-renders.
useCallback()
The useCallback hook in React is used to ensure that a specific callback function only changes if one of its dependencies has changed. This is particularly useful when the callback function is passed as a prop to a child component and the child component is re-rendered frequently.
const doSomething = useCallback(() => {
return someValue
}, [someValue])
The useCallback hook can be used in the following situations:
When you have a child component that is re-rendered frequently, and the callback function passed to it as a prop is defined in the parent component.
When you are passing a callback function as a prop to a component that is located several levels down in the component tree and you want to avoid unnecessary re-renders.
When you have a performance-sensitive component that re-renders frequently and you want to avoid unnecessary re-creating callback functions.
When you are using a callback function that is used in an effect hook, it will ensure that the effect only updates if the dependencies of the callback have changed.
By using the useCallback hook, you can ensure that the callback function is only re-created if one of its dependencies has changed, which can improve the performance of your application by avoiding unnecessary re-renders.
useMemo vs. useCallback
Both useMemo and useCallback are hooks in React that can be used to optimize the performance of your application, but they have different use cases and behaviors.
useMemo is used to memoize a value that is expensive to compute. It takes a function that calculates the value and an array of dependencies as arguments. The function will only be re-run if one of the dependencies has changed. It returns the memoized value.
useCallback is used to memoize a callback function. It takes the callback function and an array of dependencies as arguments. The function will only be re-created if one of the dependencies has changed. It returns the memoized callback function.
useMemo is used when you want to memoize a value that is expensive to compute, such as the result of a complex calculation or an API call, so it will only re-compute when one of the dependencies change.
useCallback is used when you want to memoize a callback function that is passed as a prop to a child component, so it will only re-create the callback function when one of the dependencies change, avoiding unnecessary re-rendering.
useMemo can also be used in conjunction with useEffect to only run the effect when the memoized value changes.
useCallback is often used in conjunction with useEffect to avoid running the effect when the callback function changes.
In summary, useMemo is used to memoize values, while useCallback is used to memoize functions. Both hooks help to optimize the performance of your application by avoiding unnecessary re-computations and re-renders.
useMemo vs. useCallback
Both useMemo and useCallback are hooks in React that can be used to optimize the performance of your application, but they have different use cases and behaviors.
useMemo is used to memoize a value that is expensive to compute. It takes a function that calculates the value and an array of dependencies as arguments. The function will only be re-run if one of the dependencies has changed. It returns the memoized value.
useCallback is used to memoize a callback function. It takes the callback function and an array of dependencies as arguments. The function will only be re-created if one of the dependencies has changed. It returns the memoized callback function.
useMemo is used when you want to memoize a value that is expensive to compute, such as the result of a complex calculation or an API call, so it will only re-compute when one of the dependencies change.
useCallback is used when you want to memoize a callback function that is passed as a prop to a child component, so it will only re-create the callback function when one of the dependencies change, avoiding unnecessary re-rendering.
useMemo can also be used in conjunction with useEffect to only run the effect when the memoized value changes.
useCallback is often used in conjunction with useEffect to avoid running the effect when the callback function changes.
In summary, useMemo is used to memoize values, while useCallback is used to memoize functions. Both hooks help to optimize the performance of your application by avoiding unnecessary re-computations and re-renders.
useRef Hook
https://react-hooks-cheatsheet.com/useRef
- returns a 'ref' object.
- Call signature: const refContainer = useRef(initialValueToBePersisted)
- Value is persisted in the refContainer.current property.
- values are accessed from the .current property of the returned object.
- The.current property could be initialised to an initial value e.g. useRef(initialValue)
- The object is persisted for the entire lifetime of the component.
useRef Hook
https://react-hooks-cheatsheet.com/useRef
- returns a 'ref' object.
- Call signature: const refContainer = useRef(initialValueToBePersisted)
- Value is persisted in the refContainer.current property.
- values are accessed from the .current property of the returned object.
- The.current property could be initialised to an initial value e.g. useRef(initialValue)
- The object is persisted for the entire lifetime of the component.
() => {
const textAreaEl = useRef(null);
const handleBtnClick = () => {
textAreaEl.current.value =
"The is the story of your life. You are an human being, and you're on a website about React Hooks";
textAreaEl.current.focus();
};
return (
<section style={{ textAlign: "center" }}>
<div>
<button onClick={handleBtnClick}>Focus and Populate Text Field</button>
</div>
<label
htmlFor="story"
style={{
display: "block",
background: "olive",
margin: "1em",
padding: "1em"
}}
>
The input box below will be focused and populated with some text
(imperatively) upon clicking the button above.
</label>
<textarea ref={textAreaEl} id="story" rows="5" cols="33" />
</section>
);
};
() => {
const textAreaEl = useRef(null);
const handleBtnClick = () => {
textAreaEl.current.value =
"The is the story of your life. You are an human being, and you're on a website about React Hooks";
textAreaEl.current.focus();
};
return (
<section style={{ textAlign: "center" }}>
<div>
<button onClick={handleBtnClick}>Focus and Populate Text Field</button>
</div>
<label
htmlFor="story"
style={{
display: "block",
background: "olive",
margin: "1em",
padding: "1em"
}}
>
The input box below will be focused and populated with some text
(imperatively) upon clicking the button above.
</label>
<textarea ref={textAreaEl} id="story" rows="5" cols="33" />
</section>
);
};
Rules of Hooks
- Only Call Hooks at the Top Level. Don’t call Hooks inside loops, conditions, or nested functions.
- Only Call Hooks from React Functions
- ✅ Call Hooks from React function components.
- ✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
useTransition - lets you mark a state transition as non-blocking and allow other updates to interrupt it.
useDeferredValue - lets you defer updating a non-critical part of the UI and let other parts update first.
useDebugValue lets you customize the label React DevTools displays for your custom Hook.
useId lets a component associate a unique ID with itself. Typically used with accessibility APIs.
useSyncExternalStore lets a component subscribe to an external store.
useTransition - lets you mark a state transition as non-blocking and allow other updates to interrupt it.
useDeferredValue - lets you defer updating a non-critical part of the UI and let other parts update first.
useDebugValue lets you customize the label React DevTools displays for your custom Hook.
useId lets a component associate a unique ID with itself. Typically used with accessibility APIs.
useSyncExternalStore lets a component subscribe to an external store.
Data fetching with hooks - list of items https://www.robinwieruch.de/
import React, { useState } from 'react';
function App() {
const ___ ___, ___ ___ = ___({ hits: ___ });
return (
<ul>
{data.___.___(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
Data fetching with hooks - list of items
import React, { useState } from 'react';
function App() {
const [data, setData] = useState({ hits: [] });
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
Basic axios data fetching, on initial mount
function App() {
const [data, ___] = ___(___ hits: ___ ___);
___(() => {
___ ___ = ___ () => {
const result = ___ ___(
'https://hn.algolia.com/api/v1/search?query=redux',
);
___(result.data);
};
___();
}, ___);
return (
<ul>
___data.___.___(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))___
</ul>
);
}
Basic axios data fetching, on initial mount
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(async () => {
const fetchData = async () => {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
};
fetchData();
}, []);
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
HOW TO TRIGGER A HOOK PROGRAMMATICALLY / MANUALLY?
function App() {
const [data, setData] = useState(___ hits: ___ ___);
const [query, setQuery] = useState('redux');
const [___, ___] = useState(
'___',
);
useEffect(() => {
const fetchData = ___ () => {
const result = ___ ___(___);
___(result.data);
};
___();
}, [___]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => ___(___.___.___)}
/>
<button
type="button"
onClick={() =>
___(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
}
HOW TO TRIGGER A HOOK PROGRAMMATICALLY / MANUALLY?
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
useEffect(() => {
const fetchData = async () => {
const result = await axios(url);
setData(result.data);
};
fetchData();
}, [url]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
}
LOADING INDICATOR WITH REACT HOOKS - part 1
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
const [___, ___] = ___(___);
useEffect(() => {
const fetchData = async () => {
___(___);
const result = await axios(url);
setData(result.data);
___(___);
};
fetchData();
}, [url]);
LOADING INDICATOR WITH REACT HOOKS - part 1
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
const result = await axios(url);
setData(result.data);
setIsLoading(false);
};
fetchData();
}, [url]);
LOADING INDICATOR WITH REACT HOOKS - part 2
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
{___ ? (
<div>Loading ...</div>
) : (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
LOADING INDICATOR WITH REACT HOOKS - part 2
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
{isLoading ? (
<div>Loading ...</div>
) : (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
ERROR HANDLING WITH REACT HOOKS
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
const [isLoading, setIsLoading] = useState(false);
const [___, ___] = useState(___);
useEffect(() => {
const fetchData = async () => {
___(___);
setIsLoading(true);
___ {
const result = await axios(url);
setData(result.data);
} ___ (___) {
___(___);
}
setIsLoading(false);
};
fetchData();
}, [url]);
ERROR HANDLING WITH REACT HOOKS
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
FETCHING DATA WITH FORMS AND REACT
function App() {
...
return (
<Fragment>
<___ ___={___ => {
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
___.___();
}}>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<___ type="___">Search</___>
</form>
{isError && <div>Something went wrong ...</div>}
...
</Fragment>
);
}
FETCHING DATA WITH FORMS AND REACT
function App() {
...
return (
<Fragment>
<form onSubmit={event => {
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
event.preventDefault();
}}>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
...
</Fragment>
);
}
REDUCER HOOK FOR DATA FETCHING - 1
const dataFetchReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return {
...state, isLoading: true, isError: false
};
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false, isError: false, data: action.payload,
};
case 'FETCH_FAILURE':
return {
...state,isLoading: false, isError: true,
};
default:
throw new Error();
}
};
const useDataApi = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
data: initialData,
});
useEffect(() => {
const fetchData = async () => {
dispatch({ type: 'FETCH_INIT' });
try {
const result = await axios(url);
dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
} catch (error) {
dispatch({ type: 'FETCH_FAILURE' });
}
};
fetchData();
}, [url]);
...
return [state, setUrl];
};
REDUCER HOOK FOR DATA FETCHING - 2
REDUCER HOOK FOR DATA FETCHING - 1
const dataFetchReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return {
...state, isLoading: true, isError: false
};
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false, isError: false, data: action.payload,
};
case 'FETCH_FAILURE':
return {
...state,isLoading: false, isError: true,
};
default:
throw new Error();
}
};
const useDataApi = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
data: initialData,
});
useEffect(() => {
const fetchData = async () => {
dispatch({ type: 'FETCH_INIT' });
try {
const result = await axios(url);
dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
} catch (error) {
dispatch({ type: 'FETCH_FAILURE' });
}
};
fetchData();
}, [url]);
...
return [state, setUrl];
};
React: How to create a Custom Hook
APRIL 06, 2022
https://www.robinwieruch.de/react-custom-hook/
We will create a custom Hook called useBoolean which I almost use every time when I join a new project as a React freelancer.
React: How to create a Custom Hook
Initial idea w/o custom hook:
function App() {
const [___, ___] = React.useState(___);
const ___ = () => setToggle(___);
const ___ = () => setToggle(true);
const ___ = () => setToggle(false);
return (
<div>
<button type="button" onClick={handleToggle}>
Toggle
</button>
<button type="button" onClick={handleTrue}>
To True
</button>
<button type="button" onClick={handleFalse}>
To False
</button>
{isToggle.___()}
</div>
);
}
React: How to create a Custom Hook
Initial idea w/o custom hook:
function App() {
const [isToggle, setToggle] = React.useState(false);
const handleToggle = () => setToggle(!isToggle);
const handleTrue = () => setToggle(true);
const handleFalse = () => setToggle(false);
return (
<div>
<button type="button" onClick={handleToggle}>
Toggle
</button>
<button type="button" onClick={handleTrue}>
To True
</button>
<button type="button" onClick={handleFalse}>
To False
</button>
{isToggle.toString()}
</div>
);
}
useBoolean custom hook
const useBoolean = (___ = ___) => {
const [state, setState] = React.___(___);
const handleTrue = () => ___(true);
const handleFalse = () => ___(false);
const handleToggle = () => ___(!state);
return [
___,
{
___: ___,
___: ___,
___: ___,
},
];
};
useBoolean custom hook
const useBoolean = (initialState = false) => {
const [state, setState] = React.useState(initialState);
const handleTrue = () => setState(true);
const handleFalse = () => setState(false);
const handleToggle = () => setState(!state);
return [
state,
{
setTrue: handleTrue,
setFalse: handleFalse,
setToggle: handleToggle,
},
];
};
useBoolean implementation
function App() {
const [___, {
___,
___,
___,
}] = ___(false);
return (
<div>
<button type="button" onClick={___}>
Toggle
</button>
<button type="button" onClick={___}>
To True
</button>
<button type="button" onClick={___}>
To False
</button>
{isToggle.toString()}
</div>
);
}
useBoolean implementation
function App() {
const [isToggle, {
setToggle,
setTrue,
setFalse,
}] = useBoolean(false);
return (
<div>
<button type="button" onClick={setToggle}>
Toggle
</button>
<button type="button" onClick={setTrue}>
To True
</button>
<button type="button" onClick={setFalse}>
To False
</button>
{isToggle.toString()}
</div>
);
}
React Component with TypeScript
NOVEMBER 15, 2022
https://www.robinwieruch.de/typescript-react-component/
Convert to typescript:
const Select = ({ label, value, options, onChange }) => {
return (
<label>
{label}
<select value={value} onChange={onChange}>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</label>
);
};
Initial
const Select = ({
label,
value,
options,
onChange,
}: {
label: string;
value: string;
options: ___ label: string; value: string ___ ___;
onChange: (event: ___.___<___>) => void;
}) => {
return (
<label>
{label}
<select value={value} ___={___}>
{___.map((___) => (
<option value={option.___}>{option.___}</option>
))}
</select>
</label>
);
};
Initial
const Select = ({
label,
value,
options,
onChange,
}: {
label: string;
value: string;
options: { label: string; value: string }[];
onChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
}) => {
return (
<label>
{label}
<select value={value} onChange={onChange}>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</label>
);
};
Breakout Types:
___ Option ___ { label: string; value: string };
___ Options ___ ___;
type SelectProps = {
label: string;
value: string;
options: Options;
onChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
};
const Select___ ___.___<___> = ({
label,
value,
options,
onChange,
}) => {
return (
<label>
{label}
<select value={value} onChange={onChange}>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</label>
);
};
Breakout Types:
type Option = { label: string; value: string };
type Options = Option[];
type SelectProps = {
label: string;
value: string;
options: Options;
onChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
};
const Select: React.FC<SelectProps> = ({
label,
value,
options,
onChange,
}) => {
return (
<label>
{label}
<select value={value} onChange={onChange}>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</label>
);
};
TypeScript interface for useState
https://www.robinwieruch.de/typescript-react-usestate/
___ ___ {
email: string;
password: string;
}
const App = () => {
const [userForm, setUserForm] = ___.___<___>({
email: '',
password: '',
});
...
};
interface UserFormState {
email: string;
password: string;
}
const App = () => {
const [userForm, setUserForm] = React.useState<UserFormState>({
email: '',
password: '',
});
...
};
HOW TO DISPLAY A LIST OF ITEMS IN REACT?
https://www.robinwieruch.de/react-list-component/
const mylist = ['a', 'b', 'c'];
const App = () => (
<SimpleList list={mylist} />
);
const SimpleList = ({ list }) => (
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
const mylist = ['a', 'b', 'c'];
const App = () => (
<SimpleList list={mylist} />
);
const SimpleList = ({ list }) => (
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
HOW TO DISPLAY A LIST OF OBJECTS IN REACT?
const list = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
{
id: 'b',
firstname: 'Dave',
lastname: 'Davidds',
year: 1990,
},
];
const ComplexList = () => (
<ul>
{list.map(item => (
<li key={item.id}>
<div>{item.id}</div>
<div>{item.firstname}</div>
<div>{item.lastname}</div>
<div>{item.year}</div>
</li>
))}
</ul>
);
const list = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
{
id: 'b',
firstname: 'Dave',
lastname: 'Davidds',
year: 1990,
},
];
const ComplexList = () => (
<ul>
{list.map(item => (
<li key={item.id}>
<div>{item.id}</div>
<div>{item.firstname}</div>
<div>{item.lastname}</div>
<div>{item.year}</div>
</li>
))}
</ul>
);
HOW TO DISPLAY NESTED LISTS IN REACT?
const list = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
{
id: 'b',
firstname: 'Dave',
lastname: 'Davidds',
year: 1990,
},
];
const nestedLists = [list, list];
const NestedList = () => (
<ul>
{nestedLists.map((nestedList, index) => (
<ul key={index}>
<h4>List {index + 1}</h4>
{nestedList.map(item => (
<li key={item.id}>
<div>{item.id}</div>
<div>{item.firstname}</div>
<div>{item.lastname}</div>
<div>{item.year}</div>
</li>
))}
</ul>
))}
</ul>
);
HOW TO DISPLAY NESTED LISTS IN REACT?
const list = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
{
id: 'b',
firstname: 'Dave',
lastname: 'Davidds',
year: 1990,
},
];
const nestedLists = [list, list];
const NestedList = () => (
<ul>
{nestedLists.map((nestedList, index) => (
<ul key={index}>
<h4>List {index + 1}</h4>
{nestedList.map(item => (
<li key={item.id}>
<div>{item.id}</div>
<div>{item.firstname}</div>
<div>{item.lastname}</div>
<div>{item.year}</div>
</li>
))}
</ul>
))}
</ul>
);
REACT LIST COMPONENTS
const list = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
...
];
const App = () => <List list={list} />;
const List = ({ list }) => (
<ul>
{(list || []).map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
const ListItem = ({ item }) => (
<li>
<div>{item.id}</div>
<div>{item.firstname}</div>
<div>{item.lastname}</div>
<div>{item.year}</div>
</li>
);
REACT LIST COMPONENTS
const list = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
...
];
const App = () => <List list={list} />;
const List = ({ list }) => (
<ul>
{(list || []).map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
const ListItem = ({ item }) => (
<li>
<div>{item.id}</div>
<div>{item.firstname}</div>
<div>{item.lastname}</div>
<div>{item.year}</div>
</li>
);
Add Item
const initialList = [
'Learn React',
'Learn Firebase',
'Learn GraphQL',
];
const ListWithAddItem = () => {
const [value, setValue] = React.useState('');
const [list, setList] = React.useState(initialList);
const handleChange = event => {
setValue(event.target.value);
};
const handleSubmit = event => {
if (value) {
setList(list.concat(value));
}
setValue('');
event.preventDefault();
};
return (
<div>
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">Add Item</button>
</form>
</div>
);
};
Add Item
const initialList = [
'Learn React',
'Learn Firebase',
'Learn GraphQL',
];
const ListWithAddItem = () => {
const [value, setValue] = React.useState('');
const [list, setList] = React.useState(initialList);
const handleChange = event => {
setValue(event.target.value);
};
const handleSubmit = event => {
if (value) {
setList(list.concat(value));
}
setValue('');
event.preventDefault();
};
return (
<div>
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">Add Item</button>
</form>
</div>
);
};
useBoolean()
A simple abstraction to play with a boolean, don't repeat yourself.
The hook
interface UseBooleanOutput {
value: boolean
setValue: Dispatch<SetStateAction<boolean>>
setTrue: () => void
setFalse: () => void
toggle: () => void
}
function useBoolean(defaultValue?: boolean): UseBooleanOutput {
const [value, setValue] = useState(!!defaultValue)
const setTrue = useCallback(() => setValue(true), [])
const setFalse = useCallback(() => setValue(false), [])
const toggle = useCallback(() => setValue(x => !x), [])
return { value, setValue, setTrue, setFalse, toggle }
}
The hook
interface UseBooleanOutput {
value: boolean
setValue: Dispatch<SetStateAction<boolean>>
setTrue: () => void
setFalse: () => void
toggle: () => void
}
function useBoolean(defaultValue?: boolean): UseBooleanOutput {
const [value, setValue] = useState(!!defaultValue)
const setTrue = useCallback(() => setValue(true), [])
const setFalse = useCallback(() => setValue(false), [])
const toggle = useCallback(() => setValue(x => !x), [])
return { value, setValue, setTrue, setFalse, toggle }
}
useBoolean() usage
import { useBoolean } from 'usehooks-ts'
export default function Component() {
const { value, setValue, setTrue, setFalse, toggle } = useBoolean(false)
// Just an example to use "setValue"
const customToggle = () => setValue((x: boolean) => !x)
return (
<>
<p>
Value is <code>{value.toString()}</code>
</p>
<button onClick={setTrue}>set true</button>
<button onClick={setFalse}>set false</button>
<button onClick={toggle}>toggle</button>
<button onClick={customToggle}>custom toggle</button>
</>
)
}
useBoolean() usage
import { useBoolean } from 'usehooks-ts'
export default function Component() {
const { value, setValue, setTrue, setFalse, toggle } = useBoolean(false)
// Just an example to use "setValue"
const customToggle = () => setValue((x: boolean) => !x)
return (
<>
<p>
Value is <code>{value.toString()}</code>
</p>
<button onClick={setTrue}>set true</button>
<button onClick={setFalse}>set false</button>
<button onClick={toggle}>toggle</button>
<button onClick={customToggle}>custom toggle</button>
</>
)
}
useCounter()
import { Dispatch, SetStateAction, useState } from 'react'
interface UseCounterOutput {
count: number
increment: () => void
decrement: () => void
reset: () => void
setCount: Dispatch<SetStateAction<number>>
}
function useCounter(initialValue?: number): UseCounterOutput {
const [count, setCount] = useState(initialValue || 0)
const increment = () => setCount(x => x + 1)
const decrement = () => setCount(x => x - 1)
const reset = () => setCount(initialValue || 0)
return {
count,
increment,
decrement,
reset,
setCount,
}
}
export default useCounter
useCounter()
import { Dispatch, SetStateAction, useState } from 'react'
interface UseCounterOutput {
count: number
increment: () => void
decrement: () => void
reset: () => void
setCount: Dispatch<SetStateAction<number>>
}
function useCounter(initialValue?: number): UseCounterOutput {
const [count, setCount] = useState(initialValue || 0)
const increment = () => setCount(x => x + 1)
const decrement = () => setCount(x => x - 1)
const reset = () => setCount(initialValue || 0)
return {
count,
increment,
decrement,
reset,
setCount,
}
}
export default useCounter
useCounter() usage
import { useCounter } from 'usehooks-ts'
export default function Component() {
const { count, setCount, increment, decrement, reset } = useCounter(0)
const multiplyBy2 = () => setCount((x: number) => x * 2)
return (
<>
<p>Count is {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
<button onClick={multiplyBy2}>Multiply by 2</button>
</>
)
}
useCounter() usage
import { useCounter } from 'usehooks-ts'
export default function Component() {
const { count, setCount, increment, decrement, reset } = useCounter(0)
const multiplyBy2 = () => setCount((x: number) => x * 2)
return (
<>
<p>Count is {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
<button onClick={multiplyBy2}>Multiply by 2</button>
</>
)
}
useTimeout() Hook
import { useEffect, useRef } from 'react'
import { useIsomorphicLayoutEffect } from 'usehooks-ts'
function useTimeout(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback)
// Remember the latest callback if it changes.
useIsomorphicLayoutEffect(() => {
savedCallback.current = callback
}, [callback])
// Set up the timeout.
useEffect(() => {
// Don't schedule if no delay is specified.
// Note: 0 is a valid value for delay.
if (!delay && delay !== 0) {
return
}
const id = setTimeout(() => savedCallback.current(), delay)
return () => clearTimeout(id)
}, [delay])
}
export default useTimeout
useTimeout() Hook
import { useEffect, useRef } from 'react'
import { useIsomorphicLayoutEffect } from 'usehooks-ts'
function useTimeout(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback)
// Remember the latest callback if it changes.
useIsomorphicLayoutEffect(() => {
savedCallback.current = callback
}, [callback])
// Set up the timeout.
useEffect(() => {
// Don't schedule if no delay is specified.
// Note: 0 is a valid value for delay.
if (!delay && delay !== 0) {
return
}
const id = setTimeout(() => savedCallback.current(), delay)
return () => clearTimeout(id)
}, [delay])
}
export default useTimeout
useTimeout() Usage
import { useState } from 'react'
import { useTimeout } from 'usehooks-ts'
export default function Component() {
const [visible, setVisible] = useState(true)
const hide = () => setVisible(false)
useTimeout(hide, 5000)
return (
<div>
<p>
{visible
? "I'm visible for 5000ms"
: 'You can no longer see this content'}
</p>
</div>
)
}
useTimeout() Usage
import { useState } from 'react'
import { useTimeout } from 'usehooks-ts'
export default function Component() {
const [visible, setVisible] = useState(true)
const hide = () => setVisible(false)
useTimeout(hide, 5000)
return (
<div>
<p>
{visible
? "I'm visible for 5000ms"
: 'You can no longer see this content'}
</p>
</div>
)
}