React Hooks
React 16.8、useState、
React 16.8.0 is the first release to support Hooks. When upgrading, don’t forget to update all packages, including React DOM. React Native will support Hooks in the next stable release.
why ?
- It’s hard to reuse stateful logic between components:
render props、higher-order components: restructure your components
With Hooks, you can extract stateful logic from a component so it can be tested independently and reused.
Hooks allow you to reuse stateful logic without changing your component hierarchy.
Hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data),
- Classes confuse both people and machines
Hooks let you use more of React’s features without classes.
React components have always been closer to functions.
Hooks embrace functions, but without sacrificing the practical spirit of React.
how to do?
Hooks work side-by-side with existing code so you can adopt them gradually.
we will keep supporting class components for the foreseeable future.
新组件就用hooks吧,老的class 组件也没有必要重新写,backwards-compatible。
Hooks 概览
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
State Hook
初识
// We import the useState Hook from React. It lets us keep local state in a function component.
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
/* // 数组解构
var fruitStateVariable = useState('banana'); // Returns a pair
var fruit = fruitStateVariable[0]; // First item in a pair
var setFruit = fruitStateVariable[1]; // Second item in a pair
*/
return (
<div>
<p>You clicked {count} times</p>
// When the user clicks, we call setCount with a new value. React will then re-render the Example component, passing the new count value to it.
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useState is a Hook;
useState returns a pair: the current state value and a function that lets you update it.
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
进阶
等价的 Class 例子
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
Hooks and Function Components
// function components
const Example = (props) => {
// You can use Hooks here!
return <div />;
}
// or
function Example(props) {
// You can use Hooks here!
return <div />;
}
Hooks don’t work inside classes. But you can use them instead of writing classes.
What’s a Hook?
A Hook is a special function that lets you “hook into” React features.
e.g. : useState is a Hook that lets you add React state to function components.
If you write a function component and realize you need to add some state to it, previously you had to convert it to a class.
Now you can use a Hook inside the existing function component.
Declaring a State Variable
// in class components
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
// useState in hooks, in a function components
// In a function component, we have no this, so we can’t assign or read this.state. Instead, we call the useState Hook directly inside our component
// The only argument to the useState() Hook is the initial state.
// If we wanted to store two different values in state, we would call useState() twice.
// useState returns a pair of values: the current state and a function that updates it.
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
Reading State
// in a class
<p>You clicked {this.state.count} times</p>
// in a function
<p>You clicked {count} times</p>
Updating State
// in a class
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
// in a function
<button onClick={() => setCount(count + 1)}>
Click me
</button>
Effect Hook
useEffect,adds the ability to perform side effects from a function component.
It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API.
Effects are declared inside the component so they have access to its props and state.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Effects may also optionally specify how to “clean up” after them by returning a function.
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
多个effect 时,
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
// ...
Hooks 规则
Hooks are JavaScript functions, but they impose two additional rules:
Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.
Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions.
一个插件:linter plugin
创建自己的Hooks
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
// component 1
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
// component 2
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
Hooks 约定俗成的一些东西:
Custom Hooks are more of a convention than a feature.
If a function’s name starts with ”use” and it calls other Hooks, we say it is a custom Hook.
The useSomething naming convention is how our linter plugin is able to find bugs in the code using Hooks.
其他的Hooks-Hooks API
a few less commonly used built-in Hooks that you might find useful.
如:useContext lets you subscribe to React context without introducing nesting。
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
useReducer lets you manage local state of complex components with a reducer:
function Todos() {
const [todos, dispatch] = useReducer(todosReducer);
// ...