-
useState
const [state, setState] = useState(initialState);返回一个 state,以及更新 state 的函数。
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
我们可以使用多个 state 变量,并给不同的 state 变量取不同的名称:
function ExampleWithManyStates() {
// 声明多个 state 变量
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
//...
}
我们还可以单独更新其中一个
function handleSetFruitClick() {
// 和 this.setState({ fruit: 'orange' }) 类似
setFruit('orange');
}
-
useEffect
useEffect Hook 可以看做React class 的生命周期函数里面的 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
它在第一次渲染之后和每次更新之后都会执行。import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
有一些副作用的操作需要在组件卸载的时候去清除的时候,useEffect函数为我们提供了一个钩子函数:
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); //在组件卸载的时候,会执行下面的方法,来清除之前的副作用 return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
你也可以使用多个 effect。这会将不相关逻辑分离到不同的 effect,例如:
function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); // ... }
使用class 来写react组件的时候,我们一般会从props 读取了信息的时候,当prop 发生变化,我们需要在componentDidUpdate 里面去处理:
componentDidMount() { ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentDidUpdate(prevProps) { // 取消订阅之前的 friend.id ChatAPI.unsubscribeFromFriendStatus( prevProps.friend.id, this.handleStatusChange ); // 订阅新的 friend.id ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); }
componentDidUpdate 里面的处理,这一步经常被遗忘,是 React 应用中常见的 bug 来源。
如果我们使用Hook,可以很好的避免这个问题,且不需要额外的处理。
-
通过跳过 Effect 进行性能优化
useEffect的第二个参数为可选,其作用为控制某些值变化后才执行:useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 仅在 count 更改时更新
当我们期望的值没有发生变化的时候,不会执行useEffect里面的逻辑。
注意在单个组件中使用多个 State Hook 或 Effect Hook的时候,一定要注意其顺序要一一对应,这是React内部State 和 Effect 匹配的唯一依据。
// ------------
// 首次渲染
// ------------
useState('Mary') // 1. 使用 'Mary' 初始化变量名为 name 的 state
useEffect(persistForm) // 2. 添加 effect 以保存 form 操作
useState('Poppins') // 3. 使用 'Poppins' 初始化变量名为 surname 的 state
useEffect(updateTitle) // 4. 添加 effect 以更新标题
// -------------
// 二次渲染
// -------------
useState('Mary') // 1. 读取变量名为 name 的 state(参数被忽略)
useEffect(persistForm) // 2. 替换保存 form 的 effect
useState('Poppins') // 3. 读取变量名为 surname 的 state(参数被忽略)
useEffect(updateTitle) // 4. 替换更新标题的 effect
Hook 的使用规则
- 只在最顶层使用 Hook
不要在循环,条件或嵌套函数中调用 Hook
- 只在 React 函数中调用 Hook
不要在普通的 JavaScript 函数中调用 Hook
官方推荐使用eslint-plugin-react-hooks强行检查,避免因使用不当产生的bug。