Hook
是 React 16.8
的新增特性。它可让你在不编写 class
的状况下使用 state
以及其余的 React
特性。react
这个例子用来显示一个计数器。当你点击按钮,计数器的值就会增长:数组
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> ); }
useState
惟一的参数就是初始 state
。在上面的例子中,咱们的计数器是从零开始的,因此初始 state
就是 0。浏览器
你能够在一个组件中屡次使用 State Hook
:性能优化
function ExampleWithManyStates() { // 声明多个 state 变量! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... }
你以前可能已经在 React
组件中执行过数据获取、订阅或者手动修改过 DOM
。咱们统一把这些操做称为“反作用”,或者简称为“做用”。函数
useEffect
就是一个 Effect Hook
,给函数组件增长了操做反作用的能力。它跟 class
组件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具备相同的用途,只不过被合并成了一个 API
。性能
能够通知 React
跳过对 effect
的调用,只要传递数组做为 useEffect
的第二个可选参数便可:优化
例如,下面这个组件在 React
更新 DOM
后会设置一个页面标题:ui
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 至关于 componentDidMount 和 componentDidUpdate: useEffect(() => { // 使用浏览器的 API 更新页面标题 document.title = `You clicked ${count} times`; }[count]); // 仅在 count 更改时更新 return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
当你调用 useEffect
时,就是在告诉 React
在完成对 DOM
的更改后运行你的“反作用”函数。因为反作用函数是在组件内声明的,因此它们能够访问到组件的 props
和 state
。默认状况下,React
会在每次渲染后调用反作用函数 —— 包括第一次渲染的时候。spa
反作用函数还能够经过返回一个函数来指定如何“清除”反作用。例如,在下面的组件中使用反作用函数来订阅好友的在线状态,并经过取消订阅来进行清除操做:code
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'; }
在这个示例中,React
会在组件销毁或者后续渲染从新执行反作用函数时取消对 ChatAPI
的订阅。(若是传给 ChatAPI
的 props.friend.id
没有变化,你也能够告诉 React
跳太重新订阅。)
Hook
就是 JavaScript
函数,可是使用它们会有两个额外的规则:
有时候咱们会想要在组件之间重用一些状态逻辑。目前为止,有两种主流方案来解决这个问题:高阶组件和 render props
。自定义 Hook
可让你在不增长组件的状况下达到一样的目的。
前面,咱们介绍了一个叫 FriendStatus
的组件,它经过调用 useState
和 useEffect
的 Hook
来订阅一个好友的在线状态。假设咱们想在另外一个组件里重用这个订阅逻辑。
首先,咱们把这个逻辑抽取到一个叫作 useFriendStatus
的自定义 Hook
里:
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; }
它将 friendID
做为参数,并返回该好友是否在线:
如今咱们能够在两个组件中使用它:
function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); }
这两个组件的 state
是彻底独立的。Hook
是一种复用状态逻辑的方式,它不复用 state
自己。事实上 Hook
的每次调用都有一个彻底独立的 state
—— 所以你能够在单个组件中屡次调用同一个自定义 Hook
。
自定义 Hook
更像是一种约定而不是功能。若是函数的名字以 “use”
开头并调用其余 Hook
,咱们就说这是一个自定义 Hook
。
useState
的替代方案。它接收一个形如 (state, action) => newState
的 reducer
,并返回当前的 state
以及与其配套的 dispatch
方法。(若是你熟悉 Redux
的话,就已经知道它如何工做了。)
在某些场景下,useReducer
会比 useState
更适用,例如 state
逻辑较复杂且包含多个子值,或者下一个 state
依赖于以前的 state
等。而且,使用 useReducer
还能给那些会触发深更新的组件作性能优化,由于你能够向子组件传递 dispatch 而不是回调函数 。
如下是用 reducer
重写 useState
计数器示例:
const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); }