Hook使用介绍

6 篇文章 0 订阅
5 篇文章 0 订阅

1.Hook简介

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

例子:

import React,{useState,useEffect} from 'react';
function Example() {
    //申明一个count的useState变量
    const [count,setCount] = useState(0); //当前状态和一个更新他的函数 //0表示其初始值
    console.log("执行代码");
    return (
        <div>
            你好
            <p>请点击按钮{count}</p>
            <button onClick={()=>{setCount(count + 1)}}>
                点击
            </button>
        </div>
    )
}

export default Example;

注意:React 16.8.0 是第一个支持 Hook 的版本。升级时,请注意更新所有的 package,包括 React DOM。

特点:
1.完全可选的,你无需重写任何代码就可以在组件中尝试Hook。
2.100%向后兼容,Hook不包含任何破坏性改动。
3.现在可用,Hook发布于v16.8.0,并且没有计划从React中移出class。
4.Hook不会影响你对React概念的理解,恰恰相反,Hook为已知的React提供了更直接的API:props,state,context,refs以及生命周期。Hook提供了更强大的方式来组合他们。

2.使用 State Hook

等价的class实例

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>点击数 {this.state.count}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          点击
        </button>
      </div>
    );
  }
}

Hook和函数组件

React的函数组件是这样的:

const Example = (props) => {
  // 你可以在这使用 Hook
  return <div />;
}
//或者是这样的
function Example(props) {
  // 你可以在这使用 Hook
  return <div />;
}

上面代码我们之间叫它无状态组件,但是现在我们引入了使用React state的能力,所以现在可以叫它函数组件。

注意:Hook在class内部是不起作用的。但你可以使用他们在取代class。

Hook 是什么?

在上面实例中,首选引入React中的useState的Hook

import React, { useState } from 'react';

function Example() {
  // ...
}

Hook : Hook是一个特殊的函数,他可以让你钩入React的特性。useState是允许你在React函数组件中添加state的Hook。
应用场景: 如果你在编写函数组件并意识到要向其添加一些state的时候,以前的做法是转化成class类组件,现在你可以在现有的函数组件中直接添加Hook。

声明State变量

例子:

//class
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
}
//Hook
import React, { useState } from 'react';

function Example() {
  // 声明一个叫 “count” 的 state 变量
  const [count, setCount] = useState(0);
}

从上面代码可以看出,class是在构造函数中设置this.state初始化count,而在函数组件中没有this,直接在组件中调用useState Hook。

说明:
1.useState() 方法只有唯一的参数就是初始化state,如果想要在state中存储两个不同的变量,只需要调用useState()两次即可,且每次调用都是相互独立的互不影响。
2.useState() 方法的返回值是当前state和更新state的函数。//const [count, setCount] = useState()

读取State

//class中
<p>You clicked {this.state.count} times</p>

//Hook中直接用变量count获取state的值
<p>You clicked {count} times</p>

更新State

//class中
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
   Click me
</button>
//Hook中直接用setCount方法设置count的值
<button onClick={() => setCount(count + 1)}>
   Click me
</button>

总结

方括号的作用?

const [count, setCount] = useState(0);

回答: 等号左边的部分并不是React API的一部分,名字可以自己取,这种写法是ES6中的数组解构,说明我们创建了count,和setCount两个变量,useState返回的是一个数组,返回数组的第一个值赋值给count,第二个值赋值给setCount。

使用多个 state 变量

const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
function handleOrangeClick() {
    // 和 this.setState({ fruit: 'orange' }) 类似
    setFruit('orange');
  }

在以上代码中,我们有局部变量 age,fruit 和 todos,并且我们可以单独更新它们。
你不必使用多个 state 变量。State 变量可以很好地存储对象和数组,因此,你仍然可以将相关数据分为一组。然而,不像 class 中的 this.setState,更新 state 变量总是替换它而不是合并它。

3.使用 Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `title点击数 ${count}`;
  });
  return (
    <div>
      <p>点击数{count}</p>
      <button onClick={() => setCount(count + 1)}>
        点击
      </button>
    </div>
  );
}

数据获取,设置以及手动更改 React 组件中的 DOM 都属于副作用。不管你知不知道这些操作,或是“副作用”这个名字,应该都在组件中使用过它们。

提示:如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

在 React 组件中有两种常见副作用操作:需要清除的和不需要清除的。

无需清除的 effect

有时候,我们只想在React更新DOM之后运行一些额外的代码。比如网络请求,手动变更DOM,记录日志,这些都是常见的无需清除的操作。

使用 Hook 示例:

import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>点击数 {count}</p>
      <button onClick={() => setCount(count + 1)}>
       点击
      </button>
    </div>
  );
}

useEffect 做了什么?
回答: 通过这个Hook,你可以告诉React组件需要在渲染后执行那些操作,React会保留你传递的effect,并且在执行DOM更新后去调用它。

为什么在组件内部调用 useEffect?
回答: 将useEffect放在组件内部这样就可以在effect中直接访问state或者其他props,因为他在同一个作用域中。

useEffect 会在每次渲染后都执行吗?
回答: 默认情况下他在第一次渲染和每次更新之后都会执行。

详细说明:
我们声明了 count state 变量,并告诉 React 我们需要使用 effect。紧接着传递函数给 useEffect Hook。此函数就是我们的 effect。我们可以在 effect 中获取到最新的 count 值,因为他在函数的作用域内。当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染。
传递给 useEffect 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 count 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成新的 effect,替换掉之前的。某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。

提示:
与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。

如何清除 effect

effect返回一个函数,React将会执行清除操作。
实例:

import React,{useState,useEffect} from 'react';
import useFriendState from "../../../components/ui/useFriendState/useFriendState"
function FriendStatus(props) {
    const friend = useFriendState(1,props.boolean);
    return (
        <div>
            {friend}
        </div>
    )
}
function FriendListItem(props) {
    const friend = useFriendState(1,props.boolean);
    return (
        <div>
            {friend}
        </div>
    )
}
const friendList = [
    { id: 1, name: 'Phoebe' },
    { id: 2, name: 'Rachel' },
    { id: 3, name: 'Ross' },
];
function Example() {
    //申明一个count的useState变量
    const [count,setCount] = useState(0); //当前状态和一个更新他的函数 //0表示其初始值
    const [fruit,setFruit] = useState("展示fruit");
    const [todos,setTodos] = useState({todos:1});
    const [arrMap,setarrMap] = useState([1,2,3,4]);
    const [recipientID,setRecipientID] = useState(1);
    const isRecipient = useFriendState(2,recipientID);
    //相当于componentDidMount和componentWillMount
    useEffect(()=>{
        console.log("生命周期");
        setFruit("fruit1");
        document.title = `你点击的次数${count}`;
        return ()=>{
            console.log("清除函数");
            //返回的函数相当于清楚其副作用,相当于class里面的componentWillUnmount()函数
        }
    },[count]);
    console.log("执行代码");
    return (
        <div>
            你好
            <p>请点击按钮{count}</p>
            <button onClick={()=>{setCount(count + 1)}}>
                点击
            </button>
        </div>
    )
}
export default Example;

为什么要在 effect 中返回一个函数?
回答: 这是effect可选的清除机制,每个effect都可以返回一个清除函数。他们都属于effect的一部分。

React 何时清除 effect?
回答: React会在组件卸载的时候执行清除操作。effect在每次渲染完都会执行,并且在执行当前effect之前对上一个effect进行清除。

小结
useEffect 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数。其他的 effect 可能不必清除,所以不需要返回。effect Hook 使用同一个 API 来满足这两种情况。

使用多个 Effect 实现关注点分离

实例:

import React,{useState,useEffect} from 'react';
import useFriendState from "../../../components/ui/useFriendState/useFriendState"
function FriendStatus(props) {
    const friend = useFriendState(1,props.boolean);
    return (
        <div>
            {friend}
        </div>
    )
}
function FriendListItem(props) {
    const friend = useFriendState(1,props.boolean);
    return (
        <div>
            {friend}
        </div>
    )
}
const friendList = [
    { id: 1, name: 'Phoebe' },
    { id: 2, name: 'Rachel' },
    { id: 3, name: 'Ross' },
];
function Example() {
    //申明一个count的useState变量
    const [count,setCount] = useState(0); //当前状态和一个更新他的函数 //0表示其初始值
    const [fruit,setFruit] = useState("展示fruit");
    const [todos,setTodos] = useState({todos:1});
    const [arrMap,setarrMap] = useState([1,2,3,4]);
    const [recipientID,setRecipientID] = useState(1);
    const isRecipient = useFriendState(2,recipientID);
    //相当于componentDidMount和componentWillMount
    useEffect(()=>{
        console.log("生命周期");
        document.title = `你点击的次数${count}`;
    },[count]);
    useEffect(()=>{
        console.log("第二个effect",count);
        alert(count);
    },[count]);
    console.log("执行代码");
    return (
        <div>
            你好
            <p>请点击按钮{count}</p>
            <button onClick={()=>{setCount(count + 1)}}>
                点击
            </button>
        </div>
    )
}
export default Example;

使用多个 effect。这会将不相关逻辑分离到不同的 effect 中。
Hook 允许我们按照代码的用途分离他们, 而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的每一个 effect。

通过跳过 Effect 进行性能优化

在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 componentDidUpdate 中添加对 prevProps 或 prevState 的比较逻辑解决。

Hook中,如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可。

useEffect(() => {
  document.title = `点击数 ${count}`;
}, [count]); // 仅在 count 更改时更新
//如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

对于props的变化也同样适用,相当于class中的componentWillReceiveProps(nextProps)方法

//useEffect也可以监听props的变化
 useEffect(()=>{
 	//props变化需要执行的操作
 },[props]);

4.Hook 规则

Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则

1.只在最顶层使用 Hook,不要在循环,条件或嵌套函数中调用 Hook。
2.只在 React 函数中调用 Hook,不要在普通的 JavaScript 函数中调用 Hook。

一个名为 eslint-plugin-react-hooks 的 ESLint 插件来强制执行这两条规则。

npm install eslint-plugin-react-hooks --save-dev
// 你的 ESLint 配置
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
    "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
  }
}

5.自定义hook

通过自定义hook,可以将组件逻辑提取到可重用的函数的中。
到目前为止,在react中两种方式来共享组件组件之间的状态逻辑;render props和高阶组件,Hook可以让你在不增加组件的情况下解决相同问题。

提取自定义Hook

自定义Hook将两个函数组件的共用逻辑提取到第三个函数中。

自定义Hook是一个函数,其名称要以“use”开头,函数内部可以调用其他Hook。

例如:

import React,{useState,useEffect} from "react";
function useFriendState(friend) {
    const [isOnline,setIsOnline] = useState(null);
    useEffect(()=>{
        setIsOnline(friend ? "true" : "false");
    });
    return isOnline
}
export default useFriendState;

与React类组件不同的是,自定义Hook不需要有特殊的标识。可以自由的决定它的参数是什么,返回什么样的值。其实就和正常的函数一样,但它的名字应该始终要以use开头。

使用自定义Hook

例如:

//Home.js
import React,{useState,useEffect} from 'react';
import useFriendState from "../../../components/ui/useFriendState/useFriendState"
function FriendStatus(props) {
    const friend = useFriendState(props.boolean);
    return (
        <div>
            {friend}
        </div>
    )
}
function FriendListItem(props) {
    const friend = useFriendState(props.boolean);
    return (
        <div>
            {friend}
        </div>
    )
}
function Example(){
    return (
        <div>
            <div>6.自定义Honk</div>
            <FriendStatus boolean={true} />
            <FriendListItem boolean={false} />
        </div>
    )
}

export default Example;

特点:
1.自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。
2.自定义 Hook 必须以 “use” 开头,不然会违反Hook规则。
3.自定义 Hook 使用的是一种重用状态逻辑的机制,每次使用自定义 Hook 的所有state和副作用都是完全隔离的。
4.自定义 Hook 是独立获取state的,useState 和 useEffect也是完全独立的,正如我们在组件内可以多次调用useState和useEffect一样。

多个Hook之间传递信息

由于Hook本身就是函数,所以我们可以在他之间传递信息。
例如:

//useFriendState.js
const friendList = [
    { id: 1, name: 'Phoebe' },
    { id: 2, name: 'Rachel' },
    { id: 3, name: 'Ross' },
];
import React,{useState,useEffect} from "react";
function useFriendState(index,friend) {
    const [isOnline,setIsOnline] = useState(null);
    useEffect(()=>{
        if(index == 1){
            setIsOnline(friend ? "true" : "false");
        }else if(index == 2){
            let name = "";
            for(let val of friendList){
                if(val.id == friend){
                    name = val.name;
                    break;
                }
            }
            setIsOnline(name);
        }
    });
    return isOnline
}
export default useFriendState;
//Home.js
import React,{useState,useEffect} from 'react';
import useFriendState from "../../../components/ui/useFriendState/useFriendState"
const friendList = [
    { id: 1, name: 'Phoebe' },
    { id: 2, name: 'Rachel' },
    { id: 3, name: 'Ross' },
];
function Example() {
    const [recipientID,setRecipientID] = useState(1);
    const isRecipient = useFriendState(2,recipientID);
    return (
        <div>
            <div>在多个Hook之间传递信息</div>
            <div>{isRecipient}</div>
            <select
                value={recipientID}
                onChange={e => setRecipientID(Number(e.target.value))}
            >
                {friendList.map(friend => (
                    <option key={friend.id} value={friend.id}>
                        {friend.name}
                    </option>
                ))}
            </select>
        </div>
    )
}

export default Example;

6.React 中内置的 Hook API

useReducer的使用

const [state, dispatch] = useReducer(reducer, initialArg, init);

useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

以下是用 reducer 重写 useState实例:

import React,{useState,useEffect,useReducer} from 'react';

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {...state,count: state.count + 1};
    case 'decrement':
      return {...state,count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  //或者 const [state, dispatch] = useReducer(reducer,{count:0} );
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

useRef的使用

const refContainer = useRef(initialValue);

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

实例:

import React,{useState,useEffect,useReducer,useRef} from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值