原文连接:Githubhtml
在 React v16.7.0 alpha 版本里,提出了一个新的 Feature Proposal :Hooks ,对社区以及之后前端发展所带来的影响是巨大的。前端
学习 Hooks 的知识须要对 React 生态有较深刻的理解vue
Hooks 是 React 内部组件中的一系列特殊函数,直观带来的改变是引入state、生命周期函数、或者其余 React 功能,无需使用 classes 编写组件(类语法带来的问题有不少),背后为前端带来更深刻更普及的 functional programming
思想。react
React 官方阐明了引入 Hooks 的动机,Hooks 出现前,咱们编写 React 组件 会常常遇到的问题:git
It’s hard to reuse stateful logic between components
组件之间共享复用有状态逻辑
,组件间逻辑的复用和数据传递就变得十分困难(必须一层一层往下传),因此咱们使用 render props
和 higher-order components
来解决复用逻辑的同时引来了新的问题,一些无关 UI 的 wrapper 组件愈来愈多,嵌套组件愈来愈深,造成 wrapper hell
,虽然 React devTools 有过滤器来帮助咱们更容易地调试。Complex components become hard to understand
添加监听器
,咱们须要在componentDidMount
与 componentWillUnmount
中分别编写添加与删除监听器的逻辑,而通常在 componentDidMount
中,咱们也会编写 请求数据
的逻辑。各类功能不相关联的逻辑写在一块儿,并且相同功能的逻辑散落在不一样函数内,这带来许多隐患以及调试上的困难Classes confuse both people and machines
this
在 JS 的工做方式,例如咱们须要 绑定事件处理程序 (以何种方式绑定这里不是重点,我的推荐箭头函数形式);另一些重要实践上,使用 Class 语法也带来诸多问题,详细参阅 classes-confuse-both-people-and-machines)目前主要的 Hooks :github
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
);
}
复制代码
使用 state hooks
在 function components
中能够像上面代码这样,等同于Class语法的代码就不贴了。数组
值得一提的是,在 Hooks 出现以前,咱们一般叫这样形式的组件为
stateless components
orstateless function components
,但如今,有了 Hooks ,咱们能够在这类组件中使用 state,因此改称function components
。闭包
当前状态
和 更新它的函数
。(这里咱们使用 array destructuring
的方式将值取出来。)建立多个 state 就像这样app
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
复制代码
与 this.setState
不一样,更新状态老是替换它而不是合并它(也解决了不少以前合并带来的问题)less
Functional updates
若是新的 state 值是依赖上一个 state 值来计算的,咱们能够给 setState
传递一个函数参数,这个函数的参数为上一个 state 的值,返回值是更新后的 state 值,例如:
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<> Count: {count} <button onClick={() => setCount(0)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); } 复制代码
因此若是须要更新的 state 值为 Object,咱们应该使用 object spread syntax
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
复制代码
若是初始化的值是须要大量计算获得的结果,可使用函数代替,此函数只会在初始化阶段执行
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
复制代码
Effect 其实就是 请求数据,操做DOM,以及订阅事件等一系列 反作用/效果
而 useEffect 则是 以前 componentDidMount
,componentDidUpdate
和componentWillUnmount
的结合
React组件中有两种常见的 Effect:须要清理和不须要清理的 Effect
import { 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>
);
}
复制代码
import { 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);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
复制代码
须要单独的 API 来执行清理逻辑。由于添加和删除订阅的逻辑是相关的,useEffect 旨在将其保持在一块儿。 若是 useEffect 返回一个函数,React 将在清理时执行它
清理的时机是
当组件卸载时
,但,useEffect 会在每次渲染后运行而不只仅是一次, 这就是 React 在下次执行 useEffect 以前还清除前一个 useEffect 的缘由;Using the Effect Hook – React
若是要减小 useEffect 内并非每次渲染都必要的逻辑,能够:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
复制代码
React 会比较两次渲染的 count 值,若是同样,就会跳过此次 useEffect
咱们能够封装在多个组件可重用的包含状态的逻辑,例如
import { 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;
}
复制代码
useFriendStatus
就是一个咱们写好的复用逻辑函数,供其余组件调用。
多个组件使用 相同自定义Hooks,它们的状态和效果是 独立隔离的,仅仅是逻辑的复用。由于本质是
调用 Custom Hooks
是调用useState
和useEffect
,它们在一个组件调用不少次,彼此产生的状态也是彻底独立的。
详细参见文档:Writing Custom Hooks – React
务必遵照的规则: Rules of Hooks – React
Hooks API: Hooks API Reference – React
React Hooks 带来的边际效应能够说是巨大的,但愿更加完善以后,能够看到打开新窗的前端。
启发 Hooks 的产生:Hooks FAQ – React
关于 Hooks 的讨论:RFC: React Hooks by sebmarkbage · Pull Request #68 · reactjs/rfcs · GitHub
有趣的是,Vue的做者也很快建立了在 Vue 实验 Hooks 的repo:GitHub - yyx990803/vue-hooks: Experimental React hooks implementation in Vue