-
Back to react
-
To understand hooks better think of a component as having 3 phases: Mount, render and unmount. source.
-
What makes a custom hook a hook is that it uses other hooks, either build-in or other custom hooks
- If you pass a function to
useStateinstead of a value, this function will only be ran on the initial mount on the component, this is handy to avoid expensive functions running on every render (I.e. running from localStorage)
-
The array passed as a second argument to
useEffectonly does a shallow comparison, so if you pass one object in that array the effect will run on every render (same as passing nothing as a second argument touseEffect). -
You use
useRef()and then pass that value as ref prop to the component you want to access later. Before mounting this ref will be undefined, so you normally want to access that insideuseEffect. Then the component is already mounted, so we can accessref.currentand do whatever we want to the component. -
The function passed as a first argument to
useEffect, can return a callback that will be called when the component unmounts. This is useful normally to remove event listeners. -
One important thing to note about the
useEffecthook is that you cannot return anything other than the cleanup function. This has interesting implications with regard to async/await syntax:
// this does not work, don't do this:
React.useEffect(async () => {
const result = await doSomeAsyncThing()
// do something with the result
})- The reason this doesn’t work is because when you make a function async, it automatically returns a promise. So if you want to use async/await, the best way to do that is like so:
React.useEffect(() => {
async function effect() {
const result = await doSomeAsyncThing()
// do something with the result
}
effect()
})-
This ensures that you don’t return anything but a cleanup function.
-
It’s typically just easier to extract all the async code into a utility function which I call and then use the promise-based
.thenmethod instead of using async/await syntax:
React.useEffect(() => {
doSomeAsyncThing().then(result => {
// do something with the result
})
})-
useReduceris a very flexible pattern to manage state, you can make it work so it takes an object, a function or both. You can also make it work like a redux reducer by passing an action object with or without payload. -
I.E. this reducer will mimic
setState:
function setStateReducer(state, action) {
return {
...state,
...(typeof action === 'function' ? action(state) : action),
}
}-
When we pass a function to
useEffect, we can include the function in the dependency list (the array that we pass as a second argument touseEffect). The problem is that it will cause the function to run on every render if the function is defined inside the component function body. Every render the function will be instantiated so we run theuseEffecton every render. -
To avoid this we define the function using
React.useCallback,the function will still be created on every render, but React will only give us the new one if the dependency list of the function registers changes (we also pass a dependency list touseCallback).
-
useContextis typically better suited for libraries than for application code (for app code is better to use composition) -
useContextavoids doing prop drilling, you can create a provider in a higher level of the tree, and use that later down that tree. Think global variables without the disadvantages.
Here's an example:
import React from 'react'
const RickContext = React.createContext()
function RickDisplay() {
const rick = React.useContext(Rick)
return <div>Rick is: {rick}</div>
}
ReactDOM.render(
<RickContext.Provider value="Pickle Rick">
<RickDisplay />
</RickContext.Provider>,
document.getElementById('root'),
)
// renders <div>Rick is: Pickle Rick</div>-
<RickDisplay />could appear anywhere in the render tree, and it will have access to the value which is passed by theRickContext.Providercomponent. -
As the first argument to
createContextyou could pass a default value that will be shown in case anyone tries to use context outside a Provider, but it's not recommended cause this (accessing context outside a provider) is probably due to the user making a mistake. -
Context is primarily used when some data needs to be accessible by many components at different nesting levels. Apply it sparingly because it makes component reuse more difficult. If you only want to avoid passing props through many levels, use component composition instead (component composition is when a component renders its props.children)
-
Context does not need to be globally available. Keeping a context value scoped to the area that needs it most has improved performance and maintainability characteristics.
-
UseLayoutEffectruns before React renders (thinkcomponentWillMount). -
UseEffectruns after React has rendered (thinkcomponentDidMount). -
If your effect is mutating the DOM (via a DOM node ref) and the DOM mutation will change the appearance of the DOM node between the time that it is rendered and your effect mutates it, then you don't want to use
useEffect. You'll want to useuseLayoutEffect. Otherwise the user could see a flicker when your DOM mutations take effect. This is pretty much the only time you want to avoiduseEffectand useuseLayoutEffectinstead. -
This runs synchronously immediately after React has performed all DOM mutations. This can be useful if you need to make DOM measurements (like getting the scroll position or other styles for an element) and then make DOM mutations or trigger a synchronous re-render by updating state
-
When to choose one:
useLayoutEffect: If you need to mutate the DOM with notable effects to the user and/or do need to perform measurementsuseEffect: If you don't need to interact with the DOM at all or your DOM changes are unobservable (seriously, most of the time you should use this).
- The normal use for this is when you want to allow parents to focus on child components using refs.
- Use with caution, prefer declarative to imperative
- It involves a pretty convoluted use of
React.forwardRef, so better to check how to do it when needed
- Used for React Dev Tools browser extension
- Imagine you are calling a hook several times, and you want to track which one is running, you can call
useDebugValueinside your hook and pass to it values (just like console.log) this values will show on React Dev Tools when the component is selected. - It only works inside hooks, not inside components
useDebugValueaccepts a formatting function as an optional second parameter. This function is only called if the Hooks are inspected. It receives the debug value as a parameter and should return a formatted display value. (use this if calculating the default value is expensive, if not it's better not to use it, since it would be more expensive to create the function).