Basic Hooks
useState
Usage and Examples (from React Docs)
- Adding state to a component
- Updating state based on the previous state
- Updating objects and arrays in state
- Avoiding recreating the initial state
- Resetting state with a key
- Storing information from previous renders
useEffect
Usage and Examples (from React Docs)
- Connecting to an external system
- Wrapping Effects in custom Hooks
- Controlling a non-React widget
- Fetching data with Effects
- Specifying reactive dependencies
- Updating state based on previous state from an Effect
- Removing unnecessary object dependencies
- Removing unnecessary function dependencies
- Reading the latest props and state from an Effect
- Displaying different content on the server and the client
createContext and useContext
Usage and Examples (from React Docs)
- Passing data deeply into the tree
- Updating data passed via context
- Specifying a fallback default value
- Overriding context for a part of the tree
- Optimizing re-renders when passing objects and functions
- Create the context with the
createContext
function containing the values you want to share between components.import { createContext } from 'react';
const MyContext = createContext<MyContextProps>({
message: '',
setMessage: () => {},
}); - Provide the context to the required components.
<MyContext.Provider value={{ message, setMessage }}>
- Then, useContext to access the context values.
const { message } = useContext(MyContext);
"createContext
import { createContext, useContext } from 'react';
interface MyContextProps {
message: string;
setMessage: (value: string) => void;
}
const MyContext = createContext<MyContextProps>({
message: '',
setMessage: () => {},
});
const ComponentOne = () => {
const { message } = useContext(MyContext);
return <div>{message}</div>;
};
const ComponentTwo = () => {
const { setMessage } = useContext(MyContext);
const handleClick = () => {
setMessage('Hello from Component Two');
};
return <button onClick={handleClick}>Set Message</button>;
};
const MyApp = () => {
const [message, setMessage] = React.useState('Hello from MyApp');
return (
<MyContext.Provider value={{ message, setMessage }}>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</MyContext.Provider>
);
};
export default MyApp;
useReducer
Usage and Examples (from React Docs)
- Define the State and Action interfaces to describe the shape of our state and actions.
- Define the reducer function, which takes the current state and an action and returns the new state based on the action.
- In the Counter component, useReducer hook to initialize our state with the initialState object and the reducer function.
- Define handleIncrement and handleDecrement functions that dispatch the corresponding actions to the reducer function.
- Render the current count and two buttons that call the handleIncrement and handleDecrement functions respectively when clicked.
useReducer
import React, { useReducer } from 'react';
interface State {
count: number;
}
interface Action {
type: 'INCREMENT' | 'DECREMENT';
payload?: number;
}
const initialState: State = {
count: 0,
};
function reducer(state: State, action: Action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + (action.payload || 1) };
case 'DECREMENT':
return { ...state, count: state.count - (action.payload || 1) };
default:
return state;
}
}
export const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const handleIncrement = () => {
dispatch({ type: 'INCREMENT' });
};
const handleDecrement = () => {
dispatch({ type: 'DECREMENT' });
};
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
</div>
);
};
combining useReducer and useContext
- Full solution with CountProvider component that wraps the Counter component and provides the state and dispatch functions to the Counter component.
useReducer and useContext
import React, { createContext, useContext, useReducer } from 'react';
type Action = { type: 'increment' } | { type: 'decrement' };
type State = { count: number };
type Dispatch = (action: Action) => void;
const initialState: State = { count: 0 };
const CountStateContext = createContext<State | undefined>(undefined);
const CountDispatchContext = createContext<Dispatch | undefined>(undefined);
function countReducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
function CountProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(countReducer, initialState);
return (
<CountStateContext.Provider value={state}>
<CountDispatchContext.Provider value={dispatch}>
{children}
</CountDispatchContext.Provider>
</CountStateContext.Provider>
);
}
function useCountState() {
const context = useContext(CountStateContext);
if (context === undefined) {
throw new Error('useCountState must be used within a CountProvider');
}
return context;
}
function useCountDispatch() {
const context = useContext(CountDispatchContext);
if (context === undefined) {
throw new Error('useCountDispatch must be used within a CountProvider');
}
return context;
}
function Counter() {
const state = useCountState();
const dispatch = useCountDispatch();
return (
<div>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
function App() {
return (
<CountProvider>
<Counter />
</CountProvider>
);
}
export default App;
Sum
const sumNumbers = numbers.reduce((sum, value) => sum + value, 0)
Average
const averageNumbers = numbers. reduce((avg, value, _, arr) => avg + value / arr.length, 0);
useRef
Usage and Examples (from React Docs)
useMemo
Usage and Examples (from React Docs)
useCallback
- cache a function definition between re-renders.
Usage and Examples (from React Docs)
- Skipping re-rendering of components
- Updating state from a memoized callback
- Preventing an Effect from firing too often
- Optimizing a custom Hook
useLayoutEffect
- version of useEffect that fires before the browser repaints the screen.
Usage and Examples (from React Docs)
useDeferredValue
React docs: "You can also apply useDeferredValue as a performance optimization. It is useful when a part of your UI is slow to re-render, "
import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<div style={{
opacity: isStale ? 0.5 : 1,
transition: isStale ? 'opacity 0.2s 0.2s linear' : 'opacity 0s 0s linear'
}}>
<SearchResults query={deferredQuery} />
</div>
</Suspense>
</>
);
}