函数式组件hooks

1、hooks概念

Hook 这个单词的意思是"钩子"。 React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。 你需要什么功能,就使用什么钩子。React 默认提供了一些常用钩子,你也可以封装自己的钩子。所有的钩子都是为函数引入外部功能,所以 React 约定,钩子一律使用use前缀命名,便于识别。你要使用 xxx 功能,钩子就命名为 useXxx。注意hook是v16.8.0后,才新增的特性

2、为什么会出现hooks

class component 学习成本高

我们在class component中要学习生命周期,React15、React16.3、React16.4到React17生命周期有了很多变化。生命周期在class组件中非常重要。但是太多的太多的生命周期难记,有些也不知道具体的场景麻烦。还有就是this指向的问题比如我们要写大量的bind函数来改变this的指向,当然也可以通过装饰器等其他方法改变,但是本质上来说还是要跟this打交道

import React from "react";
​
export default class LearnClassComponent extends React.Component {
  componentWillMount() {
    /**
     * 发生在 render 函数之前,还没有挂载 Dom
     * 已被废弃
     */
  }
​
  static getDerivedStateFromProps(nextProps, state) {
    /**
     * 在调用 render方法之前调用,在初始化和后续更新都会被调用
     * 接收两个参数第一个参数为即将更新的 新的props
     * 第二个参数为老的state ,
     * 可以比较props 和 state来加一些限制条件,防止无用的state更新
     * 返回一个对象来更新 state, 如果返回 null 则不更新任何内容
     *
     * 注意:
     * getDerivedStateFromProps 是一个静态函数,
     *  不能使用this, 也就是只能作一些无副作用的操作
     *
     */
    return state;
  }
​
  componentDidMount() {
    /**
     * 在组件挂载后 (插入DOM树后) 立即调用,
      只会调用一次,在初始化后
     * 常用于
     * 发送网络请求等,并且可以在 此钩子函数里直接调用 setState()
     */
  }
​
  /**
   *
   * @param {*} nextProps 即将更新的 props 值
   * @param {*} nextState 即将跟新后的 state 值
   * @returns 返回true则更新false则不更新视图
   */
  shouldComponentUpdate(nextProps, nextState) {
    /**
     * 在组件更新之前调用,可以控制组件是否进行更新
     * 一般常用于优化性能
     * 注意:
     * 1、不建议在 shouldComponentUpdate()
     * 中进行深层比较或使用JSON.stringify()。这样非常影响效率,且会损害性能
     * 2、不要 shouldComponentUpdate 中调用 setState(),
     * 否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃
     * 3、可以使用内置 PureComponent 组件替代
     *
     */
    return true;
  }
​
  /**
   *
   * @param {*} prevProps
   * @param {*} prevState
   * @return
   */
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log(prevProps, prevState);
    /**
     *
     * 因为render和componentDidUpdate之间可能有延迟,所以用他来补足
     * 它可以使组件在 DOM 真正更新之前捕获一些信息(例如滚动位置),
     * 此生命周期返回的任何值都会作为参数传递给 componentDidUpdate()。
     * 如不需要传递任何值,那么请返回 null
     *
     */
    return null;
  }
​
  /**
   *
   * @param {*} prevProps 上一次的或者说老的props
   * @param {*} prevState 上一次的或者说老的state
   * @param {*} snapshot 这是getSnapshotBeforeUpdate这个生命周期返回的
   */
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("我是,componentDidUpdate", prevProps, prevState, snapshot);
    /**
     * 每次触发render后都会被调用
     * 注意:
     * 不要在这里使用this.setState()等改变视图的操作,会造成死循环
     */
  }
​
  componentWillUnmount() {
    /**
     * 在组件即将被卸载或销毁时进行调用
     * 可以用于取消网络请求、移除监听事件、清理 DOM 元素、清理定时器等操作
     */
  }
​
  /**
   * 方法是class组件中唯一必须实现的方法,用于渲染dom, render()方法必须返回reactDOM
   * @returns
   */
  render() {
    return (
      
       
你好,react
     
  ); } } ​

class component 逻辑代码分散

我们在学习代码的第一天,就应该知道高内聚、低耦合这六字箴言。设计组件也是一样的,我们应当考虑代码的高可复用性。然而在class组件中,我们实现一个功能,就不得不把相同业务的一些逻辑分散到各个生命周期中,就显得逻辑分散,比如我们设置一个定时器,就要考虑在合适的生命周期里面初始化定时器,以及销毁定时器等显的逻辑很分散

react hooks 逻辑复用更加便捷

Class组件逻辑复用一般使用的都是HOCRender Props。但这两者虽然都可以实现逻辑复用,但是并没有让组件和代码变得好用和优美起来,这二者都会存在的一个问题是,逻辑的复用会导致嵌套层级过深,形成嵌套地狱。使用class组件,表单组件、国际化、Redux等混在一块形成一长串的高阶组件的形式,就很恶心

3、hooks初探

以下代码完成一个简单的点击+1的功能

// 引入对应的要使用的hooks
import React, { useState } from 'react';
​
// 导出一个函数
export default function MobileFooter() {
  // 使用引入的hooks定义一个状态,并暴露出初始状态,和修改状态的方法
  const [num, setNum] = useState(1)
  return (
    
    {/* 每次点击+1 */}           {/* 展示num的值 */}      
{num}
   
); }

4、usestate介绍

返回一个 state,以及更新 state 的函数。 在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。 setState 函数用于更新 state。它接收一个新的 state 值并将组件的依次重新渲染加入队列。 可以简单理解为,如果要改变数据联动视图就要使用useState 注意:如果你的更新函数返回值与当前 state 完全相同,则随后的重渲染会被完全跳过。

5、useState基本写法

import React, { useState } from 'react';
export default function hook() {
 // 这两个参数我们可以自定义名字,用的是数组结构的方法
  const [num, setNum] = useState(1)
​
  return (
    
           
你好,react hook{num}
   
); }

6、进阶写法

如果你的初始值比较比较复杂,则可以直接传入一个函数进行计算,该函数只会在初始化的时候被调用一次,后续在使用setState更新的时候,不会在改变值,也就是说useState是惰性的

import React, { useState } from 'react';
export default function hook() {
  const initNum = (i) => {
    return i + 1
  }

  // 这两个参数我们可以自定义名字,用的是数组结构的方法
  const [num, setNum] = useState(() => {
    console.log("只会在初始化的时候触发了")
    const initialState = initNum(1);
    return initialState;
  })

  const add = () => {
    // 改变数据不会再触发initNum
    setNum(num + 1)
  }


  return (
    
你好,react hook{num}
); }

 7、useEffect

useEffect 可以让你在函数组件中执行副作用操作,接收两个参数,第一个参数是要执行的函数 callback,第二个参数是可选的依赖项数组 dependencies。其中依赖项是可选的,如果不指定,那么 callback 就会在每次函数组件执行完后都执行;如果指定了,那么只有依赖项中的值发生变化的时候,它才会执行。 简单来说就是当我们的依赖项发生发生变化的时候,可以异步的执行里面的回调。 注意: useEffect是在render之后执行

import React, { useState, useEffect } from 'react';
export default function hook() {

  const [num, setNum] = useState(1)

  /**
   * 第一个参数是回调函数
   * 第二个参数是依赖项
   * 每次num变化时都会变化
   * 
   * 注意初始化的时候,也会调用一次
   */
  useEffect(() => {
    console.log("每次num,改变我才会触发")
    
    return () => {
      /**
       * 这是卸载的回调可以执行某些操作
       * 如果不想执行,则可以什么都不写
       */
      console.log("卸载当前监听")
    }
  }, [num])

  useEffect(() => {
    console.log("每次render页面我就会触发")
    return () => {
      console.log("卸载当前监听")
    }
  })

  return (
    
你好,react hook{num}
); }

你可能感兴趣的:(react.js,javascript,前端)