Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
Hooks are JavaScript functions, but you need to follow two rules when using them. We provide a linter plugin to enforce these rules automatically:
Only Call Hooks at the Top LevelDon’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState
and useEffect
calls. (If you’re curious, we’ll explain this in depth below.)
Don’t call Hooks from regular JavaScript functions. Instead, you can:
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.
ESLint PluginWe released an ESLint plugin called eslint-plugin-react-hooks
that enforces these two rules. You can add this plugin to your project if you’d like to try it:
This plugin is included by default in Create React App.
npm install eslint-plugin-react-hooks --save-dev
{
"plugins": [
"react-hooks"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
You can skip to the next page explaining how to write your own Hooks now. On this page, we’ll continue by explaining the reasoning behind these rules.
ExplanationAs we learned earlier, we can use multiple State or Effect Hooks in a single component:
function Form() {
const [name, setName] = useState('Mary');
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
const [surname, setSurname] = useState('Poppins');
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
}
So how does React know which state corresponds to which useState
call? The answer is that React relies on the order in which Hooks are called. Our example works because the order of the Hook calls is the same on every render:
useState('Mary')
useEffect(persistForm)
useState('Poppins')
useEffect(updateTitle)
useState('Mary')
useEffect(persistForm)
useState('Poppins')
useEffect(updateTitle)
As long as the order of the Hook calls is the same between renders, React can associate some local state with each of them. But what happens if we put a Hook call (for example, the persistForm
effect) inside a condition?
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
The name !== ''
condition is true
on the first render, so we run this Hook. However, on the next render the user might clear the form, making the condition false
. Now that we skip this Hook during rendering, the order of the Hook calls becomes different:
useState('Mary')
useState('Poppins')
useEffect(updateTitle)
React wouldn’t know what to return for the second useState
Hook call. React expected that the second Hook call in this component corresponds to the persistForm
effect, just like during the previous render, but it doesn’t anymore. From that point, every next Hook call after the one we skipped would also shift by one, leading to bugs.
This is why Hooks must be called on the top level of our components. If we want to run an effect conditionally, we can put that condition inside our Hook:
useEffect(function persistForm() {
if (name !== '') {
localStorage.setItem('formData', name);
}
});
Note that you don’t need to worry about this problem if you use the provided lint rule. But now you also know why Hooks work this way, and which issues the rule is preventing.
Next StepsFinally, we’re ready to learn about writing your own Hooks! Custom Hooks let you combine Hooks provided by React into your own abstractions, and reuse common stateful logic between different components.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4