React Hooks

667 阅读4分钟

这是我参与更文挑战的第15天,活动详情查看:更文挑战

类组件和函数组件的区别

  1. 首先类组件有this,我们可以通过this去访问类组件的属性、方法、状态和props;
  2. 类组件组件有自己的生命周期,如: componentDidMount、componentWillUnmount等。
  3. 函数组件没有内部没有this、没有自己的生命周期。

State Hook

这个例子用来显示一个计数器。当你点击按钮,计数器的值就会增加:

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

在这里,useState 就是一个 Hook (等下我们会讲到这是什么意思)。通过在函数组件里调用它来给组件添加一些内部 state。React 会在重复渲染时保留这个 state。useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并。

Effect Hook

你之前可能已经在 React 组件中执行过数据获取、订阅或者手动修改过 DOM。我们统一把这些操作称为“副作用”,或者简称为“作用”。

useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。(我们会在使用 Effect Hook 里展示对比 useEffect 和这些方法的例子。)

例如,下面这个组件在 React 更新 DOM 后会设置一个页面标题:

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

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

  // 相当于 componentDidMount 和 componentDidUpdate:
  useEffect(() => {
    // 使用浏览器的 API 更新页面标题
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。由于副作用函数是在组件内声明的,所以它们可以访问到组件的 props 和 state。默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候。(我们会在使用 Effect Hook 中跟 class 组件的生命周期方法做更详细的对比。)

副作用函数还可以通过返回一个函数来指定如何“清除”副作用。例如,在下面的组件中使用副作用函数来订阅好友的在线状态,并通过取消订阅来进行清除操作:

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

useState hook

我们在使用类组件的时候,如果想定义组件的状态我们只需要在组件的 constructor或者属性上面直接定义state就可以声明组件的状态,如下:

class A extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 1
    }
  }
}

在函数组件中我们要定义组件的state,我们应该怎么做呢?这就需要用到我们的useState方法进行函数组件state的定义:

const App = () => {
   const [count,setCount] = useState(0);
   return (
     <button onClick={() => {setCount(count+1)}}>点我</button>	
   )
}

这样就在函数组件当中定义了一个状态。

useContext hook

该钩子的作用是,在组件之间共享状态。关于Context这里不再赘述,其作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。 下面是一个例子,现在假设有A组件和B组件需要共享一个状态。

import React,{ useContext } from 'react'
const AppContext = React.createContext({})
 
const A =() => {
    const { name } = useContext(AppContext)
    return (
        <p>我是A组件的名字{name}<span>我是A的子组件{name}</span></p>
    )
}

const B =() => {
  const { name } = useContext(AppContext)
  return (
      <p>我是B组件的名字{name}</p>
  )
}

const Ceshi = () => {
  return (
    <AppContext.Provider value={{name: 'hook测试'}}>
	  <A/>
	  <B/>
    </AppContext.Provider>
  )
}
export default Ceshi

可以看到,我们可以通过hooks做状态的共享。

useRef hook

在类组件中我们要保存一个值,我们可以将这个值保存在类组件的属性上,但是在函数组件中由于没有this,我么可以将值保存到useRef当中:

import React, { useRef, useEffct } from 'react';
const Test = () => {
 const divRef = useRef();
 const countRef = useRef(0);
 useEffect(() => {
    console.log(divRef.current)
 }, [])
  return (
     <div ref={divRef} onClick={() => {  countRef.current++ } }>我是一个div</div>
  )
}

useCallback hook

在类组件当中我们可以使用类组件的属性去声明一个箭头函数方式来保证调用这个函数的props的值不会变化,下面有个错误的列子:

import React, { useCallback } from 'react';
const Test = () => {
   const onClick = () => {
      console.log('aaa')
   }
   
   return (
      <div onClick={onClick}>点我</div>
   )
}

这写有没有发现一个问题,当Test组件每次执行的更新的时候 onClick 对应的函数都是一个新的箭头,这样会照成div每次都会更新,性能不高。

import React, { useCallback } from 'react';
const Test = () => {
   const onClick = useCallback(() => {
      console.log('aaa')
   },[]);
   return (
      <div onClick={onClick}>点我</div>
   )
}

使用了useCallback以后,useCallback会存储函数,提高了性能。

useMemo hook

在大量计算的时候,我们可以使用 useMemo 来做缓存:

import React, { useCallback, useState } from 'react';
const Test = () => {
  const [length,setLength] = useState(1);
   const data = useMemo(() => {
       let str = ''; 
       for(let i = 0; i< length; i++) {
		 str+='这是一个字符串'
       }
      return str;
   },[length]);
   
     
const onClick = useCallback(() => {
      setLength(1000)
},[]);
   
   return (
      <div onClick={onClick}>点我{data}</div>
   )
}

Hooks的使用有两个原则:

  • 不要在循环,条件判断,函数嵌套中使用hooks
  • 只能在函数组件中使用hooks