Sophie Alpert and Dan Abramov made the case last week in React Conf 2018
hooks
Let’s look at what Hooks are solving.
TL; DR
Use the state and lifecycle of the Classes Components in the React function. There is no need to switch back and forth between mixin, function component, HOC component and render props, which makes the function of function component more realistic and more convenient for us to realize the separation of business logic code and the reuse of components in business.
This article covers hooks in the following ways
What are the Hooks addressing? The Hooks API is about Hooks and how to use them
What problem are 💡Hooks solving
React has been addressing the problem of separating business logic code and reusing related business logic within components.
Typically, we organize large UIs on our pages into small, independent UIs through components and top-down data flows, enabling component reuse. But it is often difficult to break into a complex component for reuse because the logic of the component is stateful and cannot be extracted from the functional component. This is especially common with animations and forms, where we can mess up a component by connecting to an external data source and then hoping to perform more operations within the component:
- It is difficult to reuse and share state-related logic in components, resulting in many large components
- Logically complex components are difficult to develop and maintain. When our component needs to deal with multiple unrelated localstates, each lifecycle function may contain a variety of unrelated logic.
- Complex patterns such as render items and advanced components.
- Due to business changes, functional components had to be changed to class components.
That’s where the Hooks come in. Hooks allow us to organize the logic inside a component into a reusable, isolated module.
To illustrate the point, here are two images from @Sunil Pai:
The React Hooks experience is the implementation of the React philosophy inside components. Previously, we only directly embodied the React philosophy in components and components, that is, clear data flow and composition form. The logic within components can be reused, without the layers of nesting brought by HOC, and without the drawbacks of mixins.
An introduction to the 💡Hooks API and how to use Hooks
Dan_abramov introduced usto three key apis for hooks at the conference: State hooks, Effect Hooks, and Custom hooks.
📌 state Hooks (useState)
The useState method brings local state to our function component, which takes a value for the initial state and returns a pair of variables. Let function components have their own components.
First, what if we wanted to implement a button +1 component with classes Component?
import React from 'react';
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
this.clickBtn = this.clickBtn.bind(this);
}
clickBtn = () => {
this.setState({
count: this.state.count + 1;
});
}
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.clickBtn}>
Click me
</button>
</div>
);
}Copy the code
What about using useState? You can see it very clearly.
Import {useState} from 'react'; function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }Copy the code
📌 Effect Hooks (useEffect)
Fangfa is used by Effect Hooks to handle actions that have side effects. This example uses FangFA to listen for window width changes
import { useState } from 'react';
function windowWidth() {
const [width, setWithd] = useState(window.innerWidth);
useEffect(() => {
const handleResize = ()=>{
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
});
return (
<p> window width is {width}</p>
)
}Copy the code
UseEffect can be passed in as a second operation to avoid the loss of performance, bypassing the change if the member variable in the second parameter array has not changed. How to pass in an empty array, so the effect is only executed when the component is mounted and unmounted.
import { useState } from 'react'; function windowWidth() { const [width, setWithd] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); }, [width]); Return (<p> window width is {width}</p>)}Copy the code
UseEffect can also perform cleanup operations such as unsubscribe by having a function return a function
import { useState } from 'react'; function windowWidth() { const [width, setWithd] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); Return () = > {/ / cancel window to monitor the width of the window changes. The removeEventListener (' resize); }}); return ( <p> window width is {width}</p> ) }Copy the code
As shown above, built-in React Hooks like useState and useEffect act as basic building blocks. We can use them directly in components, or we can combine them into custom hooks, such as useWindowWidth. Using custom Hooks feels like using the React built-in API.
📌Custom Hooks Custom components
Following the code that listens to the window size above, we continue with custom hooks to show how react hooks make logic within components reusable.
Talk is cheap, show me the code.
Function responsiveComponent(){// custom hooks const width = useWindowWidth(); Return (<p> Current window width is {width}</p>)}Copy the code
The code above is just a few lines long, and it makes it very clear that it is there to listen for changes to the current window. That is the goal of Hooks – to make components truly declarative, even if they contain state and side effects.
Let’s take a look at how to implement this custom Hook. We use the React local state to keep the current window width and use side effects to set the state when the window is resized
import { useState, useEffect} from 'react'; // custom hooks to listen window width change function useWindowWidth(){ const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); }, [width]); // return width does not change; }Copy the code
⚡ React Hooks rules
Hooks are JavaScript functions, but they impose two additional rules:
- Hooks can only be called at the top level. Do not call hooks in loops, conditions, or nested functions.
- Call Hooks only from the React feature component. Do not call a Hook from a regular JavaScript function. (There is another place to call Hooks — your own custom Hooks.)
🔌 other Hooks
There are some built-in hooks that are not commonly used. For example, useContext allows you to subscribe to the React context without introducing nesting:
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}Copy the code
An interesting repository, react-Use, contains a lot of interesting custom hooks
How do 👀hooks work
React-rex-not-magic -just- Arrays
React hooks are just an array, not magic.
How to implementuseState()
methods
Let’s use an example here to demonstrate how the implementation of state hooks works.
Let’s start with a component:
function RenderFunctionComponent() {
const [firstName, setFirstName] = useState("Rudi");
const [lastName, setLastName] = useState("Yardley");
return (
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
);
}Copy the code
The idea behind the hooks API is that you can return a setter function as a second entry in the hook function, and the setter will control the state managed by the hook.
So what does React have to do with this?
Let’s see how this works inside React. The following can be used to render specific components in an execution context. This means that the data stored here is outside of the component being rendered. This state is not shared with other components, but it remains within the scope that specific components can be rendered later.
1) initialization
Create two empty arrays: setters and state
Set the cursor to 0
Initialize: two empty arrays with Cursor 0
2) First render
Run component functionality for the first time.
Each useState() call, when first run, pushes the setter function (bound to the cursor position) to the setter array, and then pushes some state to the state array.
First render: Item written to array as cursor increment.
3) Subsequent rendering
Each subsequent rendering resets the cursor and only reads these values from each array.
Subsequent render: Items read from the array are cursor increments
4) Event handling
Each setter has a reference to its cursor position, so by triggering a call to any setter, it changes the state value for that position in the state array.
Setters “remember” their index and set memory based on it.
UseState functions are implemented through pseudocode
Here is an example of code to demonstrate the implementation:
let state = []; let setters = []; let firstRun = true; let cursor = 0; function createSetter(cursor) { return function setterWithCursor(newVal) { state[cursor] = newVal; }; Export function useState(initVal) {if (firstRun) {state.push(initVal); setters.push(createSetter(cursor)); firstRun = false; } const setter = setters[cursor]; const value = state[cursor]; cursor++; return [value, setter]; } useState function RenderFunctionComponent() {const [firstName, setFirstName] = useState("Rudi"); // cursor: 0 const [lastName, setLastName] = useState("Yardley"); // cursor: 1 return ( <div> <Button onClick={() => setFirstName("Richard")}>Richard</Button> <Button onClick={() => setFirstName("Fred")}>Fred</Button> </div> ); Function MyComponent() {cursor = 0; Return <RenderFunctionComponent />; // render } console.log(state); // Pre-render: [] MyComponent(); console.log(state); // Render first: ['Rudi', 'Yardley'] MyComponent(); console.log(state); // Next render: ['Rudi', 'Yardley'] // Click the 'Fred' button console.log(state); // After click: ['Fred', 'Yardley']Copy the code
conclusion
Hooks are in the early stages, but give us a good idea of the logic to reuse components, as you can see in React-16.7.0-alpha. 0.