热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

20210401ReactHook进一步理解

2021-04-01ReactHook进一步理解-ReactHooks是什么,为什么要用它?Hook是React16.8新增的特性,其主要目的是写函数组件时用到state、处理副

React Hooks是什么,为什么要用它?

Hook是React 16.8新增的特性,其主要目的是写函数组件时用到state、处理副作用等React的特性,可以不再编写class组件

之前使用函数组件大多是是无状态的,只能使用props参数,只能是个UI展示组件,没有逻辑状态注入state等,而使用state只能用class组件,但是使用类组件时,如果有大量的业务逻辑需要放在生命周期函数中,会使得项目越来越复杂且难以维护,class中的this问题需要绑定也是个令人棘手的问题,为了解决这些麻烦,Hook就出现了

Hook主要解决了三类问题:

  • 组件之间复用状态逻辑
  • 复杂组件变得难以理解
  • 难以理解的class

Hook使用规则

Hook本质是一类特殊的函数,但是使用它们会有两个额外的规则:

  • 只能在函数最外层调用Hook。不要再循环、条件判断或者子函数中调用
  • 只能在React的函数组件中调用Hook或者在自定义的Hook中调用。不要再其他Javascript函数中调用

React内置的Hook

基础的Hook
  • useState
const [state, setState] = useState(initialState)

返回一个state,以及更新state的函数

在初次渲染期间,返回的状态(state)与传入的第一个参数(initialState)相同,当然这个initialState也可以是一个函数,在初次渲染期间,会调用函数计算初始的state,后续不在调用

setState函数用于更新state。接收一个新的state值并将组件的一次重新渲染加入队列;当然也可以接收一个参数为preState=>{...}的函数并返回函数执行的结果

使用useState时,需要注意以下三个点:

1)useState返回的setState方法同类组件的setState一样,也是一个异步方法,需要组件更新之后,state的值才会变成新值

2)useState返回的setState并不具有类组件setState合并多个state的作用,如果state中有多个state,在更新时,其他值一同更新,可以使用ES6中展开运算符来达到合并的效果

setState(prevState => {
    // 也可以使用Object.assign方法
    return {...prevState, ...updatedValues}
})

3)同一个组件中可以使用useState创建多个state

  • useEffect
useEffect(didUpdate, [依赖项])

该Hook接收一个包含命令式、且可能含有副作用代码的函数

Effect专业术语为副作用。什么是副作用呢?网络请求、DOM操作都是副作用的一种,useEffect就是专门来处理副作用的

在类组件中副作用通常在componentDidMount和componentDidUpdate中进行处理,而useEffect就相当于componentDidMount、componentDidUpdate和componentWillUnmount的集合体

useEffect包含两个参数一个是执行时的回调函数didUpdate和依赖参数数组,并且回调函数还有一个返回函数,在里面会执行诸如清除定时器,取消订阅等的操作。通常,为防止内存泄漏,清除函数会在组件卸载之前执行。另外,如果组件多次渲染(通常如此),则在执行下一个effect之前,上一个effect就已被清除

依赖参数,其本身是一个数组,在数组中放入要依赖的数据,当这些数据有更新时,就只执行回调函数。整个生命周期过程如下:

组件挂载->执行副作用(回调函数)->组件更新->执行清理函数(回调函数里的返回函数)->执行副作用(回调函数)->组件准备卸载->执行清理函数(回调函数里的返回函数)->组件卸载

上述讲的useEffect是componentDidMount、componentDidUpdate和componentWillUnmount的集合体,如果只是单纯的只想要在挂载后、更新后、卸载前其中之一的阶段执行,可以参考一下操作:

componentDidMount:如果只是想在组件挂载后执行,可以把依赖数组置为空数组[],这样在更新时就不会执行该副作用了

componentWillUnmount:如果只想在组件卸载之前执行,同样可以把依赖参数置为空数组[],该副作用的返回函数会在卸载之前执行

componentWillUpdate:只检测更新相对比较麻烦,需要区分更新时还是挂载需要检测依赖数据和初始值是否一致,如果当前的数据和初始数据保持一致就说明是挂载阶段,当然安全起见应和上一次的值进行对比,若当前的依赖数据和上一次的依赖数据完全一样,则说明组件没有更新

  • useContext
const value = useContext(MyContext)

接收一个context对象(React.createContext的返回值)并返回context的当前值。当前的context值由上层组件中距离当前组件最近的valueprop决定

当组件上层最近的更新时,该Hook会触发重新渲染,并使用最新传递给MyContextprovider的contextvalue

调用了useContext的组件总会在context值变化时重新渲染。如果重新渲染组件的开销较大,可以通过使用useMemo来优化

额外的Hook
  • useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init)

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

  • useCallback
const memoizedCallback = useCallback(
    ()=>{
        doSomething(a,b);
    },
    [a,b]
)

返回一个memoized函数,大白话就是缓存函数

把内联回调函数以及依赖项数组作为参数传入useCallback,它将返回改回调函数的memoized版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如shouldComponentUpdate)的子组件时,它将非常有用

useCallback(fn, deps)相当于useMemo(()=>fn, deps)

  • useMemo
const memoizedValue = useMemo(() => computedExpensiveValue(a, b), [a, b])

返回一个memoized值,缓存值

把创建函数和依赖项数组作为参数传入useMemo,它仅会在某个依赖项改变时才会重新计算memoized值。这种优化有助于避免在每次渲染时都进行高额开销的计算

请记住,传入useMemo的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于useEffect的范畴

  • useRef
const refCOntainer= useRef(initialValue)

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

这是一个例子获取上一轮的props或state:

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

  const prevCountRef = useRef();
  useEffect(() => {
    prevCountRef.current = count;
  });
  const prevCount = prevCountRef.current;

  return <h1>Now: {count}, before: {prevCount}h1>;
}
  • useImperativeHandle
useImperativeHandle(red, createHandle, [deps])

useImperativeHandle可以让你在使用ref时自定义暴露给父组件的实例值。大多数情况下,应当避免使用ref这样的命令式代码。useImperativeHandle应当与forwardRef一起使用:

function FancyInput(props, ref){
    const inputRef useRef();
    useImperativeHandle(ref, ()=>({
        focus: ()=>{
            inputRef.current.focus();
        }
    }));
    return <input ref={inputRef} ... />
}
FancyInput = forwardRef(FancyInput);
  • useLayoutEffect

其函数签名与useEffect相同,但它会在所有的DOM变更之后同步调用effect。可以使用它来读取DOM布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect内部的更新计划将被同步刷新

尽可能使用标准的useEffect以避免阻塞视觉更新

自定义Hook

在class组件中,要想复用状态逻辑则使用render props高阶组件两种方法,而在Hook中可以把这些状态逻辑提取到一个可重用的函数中,也就是自定义Hook

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

例如之前一个例子获取上一轮的props和state,可以使用自定义的Hook如下:

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  return <h1>Now: {count}, before: {prevCount}h1>;
}

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

使用自定义Hook有以下几个注意点:

  1. 自定义Hook必须以“use”开头
  2. 两个组件中使用相同的Hook并不会共享state
  3. 自定义Hook获取state是完全独立的

最后,看了官方文档Ryan Florence介绍React Hook应用,写了例子自定义hook demo:

import Row from "./Row";
import React, { useContext, useEffect, useState } from "react";
import { ThemeContext } from "./context";
import "./index.css";

function Greeting(props) {
  const name = useFormInput("xiaoxu");
  const surname = useFormInput("fangfang");
  const theme = useContext(ThemeContext);
  const width = useWindowWidth();

  useDocumentTitle(name.value + "-" + surname.value);

  return (
    <section className={theme}>
      <Row label="Name">
        <input {...name} />
      Row>
      <Row label="Surname">
        <input {...surname} />
      Row>
      <Row label="Width">{width}Row>
    section>
  );
}

// 自定义副作用title-Hook
function useDocumentTitle(title) {
  useEffect(() => {
    document.title = title;
  });
}

// 自定义width-Hook
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => {
      setWidth(window.innerWidth);
    };
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  });
  return width;
}

// 自定义input-Hook
function useFormInput(name) {
  const [value, setValue] = useState(name);
  function handleChange(e) {
    setValue(e.target.value);
  }
  return {
    value,
    onChange: handleChange
  };
}

export default Greeting;

效果如下:


推荐阅读
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • React基础篇一 - JSX语法扩展与使用
    本文介绍了React基础篇一中的JSX语法扩展与使用。JSX是一种JavaScript的语法扩展,用于描述React中的用户界面。文章详细介绍了在JSX中使用表达式的方法,并给出了一个示例代码。最后,提到了JSX在编译后会被转化为普通的JavaScript对象。 ... [详细]
  • 获取时间的函数js代码,js获取时区代码
    本文目录一览:1、js获取服务器时间(动态)2 ... [详细]
  • fileuploadJS@sectionscripts{<scriptsrc~Contentjsfileuploadvendorjquery.ui.widget.js ... [详细]
  • React提供三种方式创建Refs:字符串Refs(将被废弃)回调函数RefsReact.createRef(从React16.3开始)第一种方式不推荐使用,原因在此,并且可能会在之后的版本移除。classMyComponentextendsReact.Component{constructor(props){sup ... [详细]
  • 认识Vue关于Vue的描述有不少,不外乎都会拿来与Angular和React对比,同样头顶MVVM双向数据驱动设计模式光环的Angular自然被对比的最多,但到目前为止,Angul ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 1简介本文结合数字信号处理课程和Matlab程序设计课程的相关知识,给出了基于Matlab的音乐播放器的总体设计方案,介绍了播放器主要模块的功能,设计与实现方法.我们将该设 ... [详细]
  • ①页面初始化----------收到客户端的请求,产生相应页面的Page对象,通过Page_Init事件进行page对象及其控件的初始化.②加载视图状态-------ViewSta ... [详细]
  • WPF之Binding初探
      初学wpf,经常被Binding搞晕,以下记录写Binding的基础。首先,盗用张图。这图形象的说明了Binding的机理。对于Binding,意思是数据绑定,基本用法是:1、 ... [详细]
  • 作用域链迷惑性代码vara100;functiontest(){console.log(a);}functiontestFun(){vara200;test();}不假思索的想到出 ... [详细]
  • javascript二叉树基本功能实现
    都是常用的功能。删除是最复杂的。。test ... [详细]
  • 时域|波形_语音处理基于matlab GUI音频数据处理含Matlab源码 1734期
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了语音处理基于matlabGUI音频数据处理含Matlab源码1734期相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
mobiledu2502886787
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有