React Hooks
[[Why React Hooks]]
- useState
- useEffect
- useReducer
- useCallback and useMemo
useContext
useEffect
vsuseLayoutEffect
useImperativeHandle
React.forwardRef
2 Rules of Hooks¶
- Hooks must be used in a React component
- Hooks must be declared at the top of the function
- must be called in the same order every time
- can't be in an
if
statement - see implementation of useState
useState¶
- accepts a function which is a lazy initializer!
useState
ignores the param afterward- for computationally expensive stuff (async, like getFromLocalStorage)
-
so that the expensive function only runs once¶
Problem with this¶
Solution¶
const [count, setCount] = useState(0)
- setCount(count + 1)
+ setCount(currentCount => currentCount + 1)
Implementing useState
¶
useEffect¶
- dependency array does a
===
comparison- will always re-render if you pass it an array/object
Implementing useEffect
¶
useReducer
¶
ate range greaterdhttps://beta.reactjs.org/reference/react/useReducer
function reducer(state, action) {
if (action.type === 'increment') {
return {
age: state.age + 1
};
}
throw Error('Unknown action.');
}
- first prop: the old state
- second prop: whatever you pass to
dispatch
- usually
action
- usually
- doesn't have to follow the Redux style of having actions with a
type
prop
Implementing useState
with useReducer
¶
function countReducer(state, newState) {
return newState;
}
const [count, setCount] = useReducer(countReducer, 0);
setCount(1);
useReducer vs useState¶
- useReducer might be good for more complex logic
useRef
¶
Why can't we have a direct pointer to a ref?
Why is the API like
The pointer will change
We have an object so that when the ref changes, it just changes the object pointer
Uses of useRef
- pointing to DOM elements
- the pointers to the DOM elements are re-rendered all the time
- Store state where we don't need to rerender when it changes
useRef
to access the previous props/state¶
const usePrevious = <ValueType,>(value: ValueType): ValueType | undefined => {
const ref = useRef<ValueType>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
export default usePrevious;
Usage
useCallback and useMemo¶
Use cases (https://kentcdodds.com/blog/usememo-and-usecallback)
- not have a function re-render every time so that it can be used in a useEffect dep array
- avoiding unnecessary re-renders when re-rendering is super expensive (Graphs, Charts, Animations)
useMemo
can be passed a function (just likeuseState
) and lazily calculate computationally expensive items
import { useCallback } from 'react';
const MyParent = ({ seardchTerm }) => {
const onItemClick = useCallback(event => {
...
}, [term]);
return (
<MyBigList
searchTerm={searchTerm}
onItemClick={onItemClick}
/>
);
}
- Without
useCallback
, theonItemClick
would be redefined whenever auseState
or a parent's state changes- which means
<MyBigList />
would be re-rendered every timeuseState
or a parent's state is called
- which means
useAsync
custom hook using useCallback
¶
advanced-react-hooks/02.md at main · advanced-react-hooks
???
what's the run
function that we return in useAsync
?¶
- instead of the user putting their
fetch
in auseCallback
- the
useEffect
inuseAsync
becomesrun
which is memoized withuseCallback
- they can put it in a
useEffect
andrun(result)
safeDispatch
(safe fetch) and useEffect
cleanup¶
- Memory leak when unmounting before the
fetch
finishes - it's not a class component so we can't just have a
componentWillMount
and abort thefetch
- I have to cancel subscriptions and async tasks in the useEffect cleanup function
How do you know if a component has been unmounted?¶
React.useLayoutEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
}, []);
useContext
¶
useEffect
vs useLayoutEffect
¶
- same API
- useEffect is run after pixels are painted
- useLayoutEffect is run before so it will delay painting
- can update the DOM before it paints
useImperativeHandle
¶
- ????
React.forwardRef
¶
- can pass a
ref
as a second param- instead of the first param with all the other regular props
Could you put ref
as a prop? in the first param of the component?
- yeah but that would cause a re-render
- you might not want to always re-render when the ref that's passed down to the component changes
Do you need to always use useImperativeHandle
when using React.forwardRef
?¶
useDebugValue
¶
- feels like
__repr__
in Python for custom hooks
Last update:
2023-04-24