React Hook
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>基本概念</li><li>State Hook<ul><li><ul><li>函数式更新</li></ul></li></ul></li><li>Effect Hook<ul><li><ul><li>性能优化</li></ul></li></ul></li><li>Hook 规则</li><li>自定义 Hook</li></ul></div><p></p><h1 id="基本概念">基本概念</h1>
<p>Hook 是能让你在函数组件中“钩入” React 特性的函数,它们名字通常都以 <code>use</code> 开始。</p>
<p>Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。</p>
<h1 id="state-hook">State Hook</h1>
<pre><code class="language-react">function ExampleWithManyStates() {
// 声明多个 state 变量
const = useState(42);
const = useState('banana');
const = useState([{ text: '学习 Hook' }]);
</code></pre>
<p>State 变量可以很好地存储对象和数组,因此,你仍然可以将相关数据分为一组。然而,不像 class 中的 <code>this.setState</code>,更新 state 变量总是<em>替换</em>它而不是合并它。</p>
<h3 id="函数式更新">函数式更新</h3>
<p>如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 <code>setState</code>。该函数将接收先前的 state,并返回一个更新后的值。</p>
<pre><code class="language-react">function Counter({initialCount}) {
const = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
</>
);
}
</code></pre>
<blockquote>
<p>与 class 组件中的 <code>setState</code> 方法不同,<code>useState</code> 不会自动合并更新对象。你可以用函数式的 <code>setState</code> 结合展开运算符来达到合并更新对象的效果。</p>
<pre><code class="language-react">setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});
</code></pre>
</blockquote>
<h1 id="effect-hook">Effect Hook</h1>
<p>通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。</p>
<p>你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。</p>
<blockquote>
<p>如果你熟悉 React class 的生命周期函数,你可以把 <code>useEffect</code> Hook 看做 <code>componentDidMount</code>,<code>componentDidUpdate</code> 和 <code>componentWillUnmount</code> 这三个函数的组合。</p>
<p>与 <code>componentDidMount</code> 或 <code>componentDidUpdate</code> 不同,使用 <code>useEffect</code> 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 <code>useLayoutEffect</code> Hook 供你使用,其 API 与 <code>useEffect</code> 相同。</p>
</blockquote>
<p>如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它。 React <em>会</em>在执行当前 effect 之前对上一个 effect 进行清除。</p>
<pre><code class="language-react">useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
</code></pre>
<p><strong>Hook 允许我们按照代码的用途分离他们,</strong> 而不是像生命周期函数那样。</p>
<p>在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 <code>componentDidUpdate</code> 中添加对 <code>prevProps</code> 或 <code>prevState</code> 的比较逻辑解决:</p>
<pre><code class="language-react">componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
</code></pre>
<h3 id="性能优化">性能优化</h3>
<p>你可以通知 React <strong>跳过</strong>对 effect 的调用,只要传递数组作为 <code>useEffect</code> 的第二个可选参数即可:</p>
<pre><code class="language-react">useEffect(() => {
document.title = `You clicked ${count} times`;
}, ); // 仅在 count 更改时更新
</code></pre>
<blockquote>
<p>如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组(<code>[]</code>)作为第二个参数。</p>
</blockquote>
<p>要记住 effect 外部的函数使用了哪些 props 和 state 很难。这也是为什么 <strong>通常你会想要在 effect 内部 去声明它所需要的函数。</strong></p>
<h1 id="hook-规则">Hook 规则</h1>
<ul>
<li>只能在<strong>函数最外层</strong>调用 Hook。不要在循环、条件判断或者子函数中调用。</li>
<li>只能在 <strong>React 的函数组件</strong>中调用 Hook。不要在其他 JavaScript 函数中调用。</li>
</ul>
<p>那么 React 怎么知道哪个 state 对应哪个 <code>useState</code>?答案是 React 靠的是 Hook 调用的顺序。因为我们的示例中,Hook 的调用顺序在每次渲染中都是相同的,所以它能够正常工作:</p>
<pre><code class="language-react">// ------------
// 首次渲染
// ------------
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
// ...
</code></pre>
<p>如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的<em>内部</em></p>
<h1 id="自定义-hook">自定义 Hook</h1>
<p><strong>自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。</strong>以use开头的原因是react需要检查你的 Hook 是否违反了 Hook 的规则。</p>
<p>自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。</p>
<p><strong>自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。</strong></p>
<pre><code class="language-react">import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
</code></pre>
<p>本质上是用一个公共函数将多个Hook方法进行了封装</p><br><br>
来源:https://www.cnblogs.com/goOtter/p/10959488.html
頁:
[1]