黄阿姨 發表於 2019-7-29 19:23:00

React使用hook

<p>Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。</p>
<h3 id="为什么会有hook">为什么会有hook</h3>
<ul>
<li>在组件之间复用状态逻辑很难,需要重新组织你的组件结构,抽象层组成的组件会形成“嵌套地狱”</li>
<li>复杂组件变得难以理解,各生命周期交叉副作用</li>
</ul>
<h3 id="state-hook">State Hook</h3>
<pre><code>import React, { useState } from 'react';      // 引入

function Example() {
// 声明一个叫 "count" 的 state 变量
const = useState(0);

return (
    &lt;div&gt;
      &lt;p&gt;You clicked {count} times&lt;/p&gt;// 使用
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;// 改变
      Click me
      &lt;/button&gt;
    &lt;/div&gt;
);
}
</code></pre>
<p><strong>Hook 在 class 内部是不起作用的。但你可以使用它们来取代 class 。</strong></p>
<blockquote>
<p><strong>useState 需要哪些参数?</strong></p>
</blockquote>
<p>useState() 方法里面唯一的参数就是初始 state。不同于 class 的是,我们可以按照需要使用数字或字符串对其进行赋值,而不一定是对象。在示例中,只需使用数字来记录用户点击次数,所以我们传了 0 作为变量的初始 state。(如果我们想要在 state 中存储两个不同的变量,只需调用 useState() 两次即可。)</p>
<blockquote>
<p><strong>useState 方法的返回值是什么?</strong></p>
</blockquote>
<p>返回值为:当前 state 以及更新 state 的函数。这就是我们写 const = useState() 的原因。这与 class 里面 this.state.count 和 this.setState 类似,唯一区别就是你需要成对的获取它们。</p>
<p>我们声明了一个叫 count 的 state 变量,然后把它设为 0。React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。我们可以通过<strong>调用 setCount 来更新当前的 count</strong>。</p>
<blockquote>
<p><strong>读取 State</strong></p>
</blockquote>
<p>当我们想在 class 中显示当前的 count,我们读取   this.state.count:</p>
<pre><code>&lt;p&gt;You clicked {this.state.count} times&lt;/p&gt;
</code></pre>
<p>在函数中,我们可以直接用 count:</p>
<pre><code>&lt;p&gt;You clicked {count} times&lt;/p&gt;
</code></pre>
<blockquote>
<p><strong>更新 State</strong></p>
</blockquote>
<p>在 class 中,我们需要调用 this.setState() 来更新 count 值:</p>
<pre><code>&lt;button onClick={() =&gt; this.setState({ count: this.state.count + 1 })}&gt;
    Click me
&lt;/button&gt;
</code></pre>
<p>在函数中,我们已经有了 setCount 和 count 变量,所以我们不需要 this:</p>
<pre><code>&lt;button onClick={() =&gt; setCount(count + 1)}&gt;
    Click me
&lt;/button&gt;
</code></pre>
<blockquote>
<p><strong>使用多个 state 变量</strong></p>
</blockquote>
<pre><code>function ExampleWithManyStates() {
// 声明多个 state 变量
    const = useState(42);
    const = useState('banana');
    const = useState([{ text: '学习 Hook' }]);
</code></pre>
<p>在以上组件中,我们有局部变量 age,fruit 和 todos,并且我们可以单独更新它们:</p>
<pre><code>function handleOrangeClick() {
    // 和 this.setState({ fruit: 'orange' }) 类似
    setFruit('orange');
}
</code></pre>
<h3 id="effect-hook">Effect Hook</h3>
<p>Effect Hook 可以让你在函数组件中执行副作用操作</p>
<pre><code>import React, { useState, useEffect } from 'react';

function Example() {
const = useState(0);

// Similar to componentDidMount and componentDidUpdate:
useEffect(() =&gt; {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
});

return (
    &lt;div&gt;
      &lt;p&gt;You clicked {count} times&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
      Click me
      &lt;/button&gt;
    &lt;/div&gt;
);
}
</code></pre>
<p><strong>可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。</strong></p>
<p>使用 class 的示例</p>
<pre><code>class Example extends React.Component {
state = {
      count: 0
}


componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
}

componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
}

render() {
    return (
      &lt;div&gt;
      &lt;p&gt;You clicked {this.state.count} times&lt;/p&gt;
      &lt;button onClick={() =&gt; this.setState({ count: this.state.count + 1 })}&gt;
          Click me
      &lt;/button&gt;
      &lt;/div&gt;
    );
}
}
</code></pre>
<p><strong>注意,在这个 class 中,我们需要在两个生命周期函数中编写重复的代码。</strong></p>
<p>使用 Hook 的示例</p>
<pre><code>import React, { useState, useEffect } from 'react';

function Example() {
const = useState(0);

useEffect(() =&gt; {
    document.title = `You clicked ${count} times`;
});

return (
    &lt;div&gt;
      &lt;p&gt;You clicked {count} times&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
      Click me
      &lt;/button&gt;
    &lt;/div&gt;
);
}
</code></pre>
<blockquote>
<p>为什么在组件内部调用 useEffect?</p>
</blockquote>
<p>将 useEffect 放在组件内部让我们可以在 effect 中直接访问 count state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。</p>
<blockquote>
<p>useEffect 会在每次渲染后都执行吗?</p>
</blockquote>
<p>是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。</p>
<p>与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect <strong>不会阻塞浏览器更新屏幕</strong>,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 useLayoutEffect Hook 供你使用,其 API 与 useEffect 相同。</p>
<p><strong>需要清除的 effect</strong></p>
<p>在 React class 中,你通常会在 componentDidMount 中设置订阅,并在 <strong>componentWillUnmount</strong> 中<strong>清除</strong>它。</p>
<blockquote>
<p>使用 Hook 的示例</p>
</blockquote>
<p>你可能认为需要单独的 effect 来执行清除操作。但由于添加和删除订阅的代码的紧密性,所以 useEffect 的设计是在同一个地方执行。如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它:</p>
<pre><code>import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
const = useState(null);

useEffect(() =&gt; {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    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';
}
</code></pre>
<p><strong>为什么要在 effect 中返回一个函数?</strong></p>
<p>这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。</p>
<p><strong>React 何时清除 effect?</strong></p>
<p>React 会在<strong>组件卸载</strong>的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React 会在执行当前 effect 之前对上一个 effect 进行清除。</p>
<p>并不是必须为 effect 中返回的函数命名。这里我们将其命名为 cleanup 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给起一个别的名字。</p>
<pre><code>React.useEffect(() =&gt; {
    const handler = () =&gt; {
      const width = `calc(100% - 80)`;
      setWidth(width);
    };
    window.addEventListener('resize', handler);
    return () =&gt; window.removeEventListener('resize', handler);
}, );
</code></pre>
<h4 id="提示-通过跳过-effect-进行性能优化">提示: 通过跳过 Effect 进行性能优化</h4>
<p>在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 componentDidUpdate 中添加对 prevProps 或 prevState 的比较逻辑解决:</p>
<p>如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:</p>
<pre><code>useEffect(() =&gt; {
document.title = `You clicked ${count} times`;
}, ); // 仅在 count 更改时更新
</code></pre>
<p>如果 count 的值是 5,而且我们的组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 和后一次渲染的 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。</p>
<blockquote>
<p>对于有清除操作的 effect 同样适用:</p>
</blockquote>
<pre><code>useEffect(() =&gt; {
function handleStatusChange(status) {
    setIsOnline(status.isOnline);
}

ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () =&gt; {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, ); // 仅在 props.friend.id 发生变化时,重新订阅
</code></pre>
<p>如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])</p>
<h3 id="hook-规则">Hook 规则</h3>
<ul>
<li>只在最顶层使用 Hook<br>
(<strong>不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。</strong>)</li>
<li>只在 React 函数中调用 Hook</li>
</ul>
<pre><code> // 🔴 在条件语句中使用 Hook 违反第一条规则
if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
}
</code></pre>
<h3 id="eslint-插件">ESLint 插件</h3>
<pre><code>npm install eslint-plugin-react-hooks --save-dev
</code></pre>
<pre><code>// 你的 ESLint 配置
{
"plugins": [
    // ...
    "react-hooks"
],
"rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
    "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
}
}
</code></pre>
<h4 id="可以在单个组件中使用多个-state-hook-或-effect-hook">可以在单个组件中使用多个 State Hook 或 Effect Hook</h4>
<pre><code>function Form() {
// 1. Use the name state variable
const = useState('Mary');

// 2. Use an effect for persisting the form
useEffect(function persistForm() {
    localStorage.setItem('formData', name);
});

// 3. Use the surname state variable
const = useState('Poppins');

// 4. Use an effect for updating the title
useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
});

// ...
}
</code></pre>
<h2 id="提取自定义-hook">提取自定义 Hook</h2>
<p>目前为止,在 React 中有两种流行的方式来共享组件之间的状态逻辑: render props 和高阶组件,现在让我们来看看 Hook 是如何在让你不增加组件的情况下解决相同问题的。</p>
<p>当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。</p>
<p><strong>自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。</strong></p>
<p>注意:</p>
<ul>
<li>自定义 Hook 必须以 “use” 开头</li>
<li>在两个组件中使用相同的 Hook <strong>不会</strong> 共享 state</li>
<li>自定义 Hook 每次调用 Hook,它都会获取独立的 state</li>
</ul>
<pre><code>// 使用该语法糖则要求为class
// @connect(({ hooks }) =&gt; ({
//   modelMsg:hooks.modelMsg,
// }))


// hook时使用该种方法引用props
export default connect(({ hooks }) =&gt; ({
modelMsg:hooks.modelMsg,
}))(HookProps);

</code></pre>
<p>本文只是对hook的一些基础进行记录,更多属性和方法可参考官方文档:https://react.docschina.org/docs/hooks-intro.html</p><br><br>
来源:https://www.cnblogs.com/adoctors/p/11263555.html
頁: [1]
查看完整版本: React使用hook