2019-04-27 | UNLOCK | 更新时间:2019-6-11 17:26

React 16.8 新增特性 Hook

在 react 的开发过程中,相信大家都会遇到一些问题,比如:组件的复用,复杂组件的嵌套传值,以及表现在 HTML 上面的多余标签嵌套等等,当然解决问题的办法大家都有。但是可能不是很好用,那么 react 就在 16.8.0 版本为大家增加了一个 Hook 特性用于解决某些问题。大家在使用前,需要将 react 提升至 16.8.0 版本或更新的版本

学习之前

在react文档中,这样写到“ Hooks solve a wide variety of seemingly unconnected problems in React that we’ve encountered over five years of writing and maintaining tens of thousands of components.” Hooks解决了五年来各种不相关的问题,我不知道有没有夸张的成分,但是Hooks 确实在更新并完善着 react ,呐。既然更新了。那么就要去学习

Hook对于State的改变

在React 中组件分为有状态组件(class)和无状态组件(function),有状态组件有着生命周期方法和状态(state)。而无状态组件则没有这些。React的思想是把页面拆成单独的可复用的组件,但是项目中各组件的沉重繁琐,它们有着各自独立的状态(state),想要复用这些组件就显得很麻烦。所以Hook 给无状态组件(function)带来了 状态方法(useState)和生命周期方法(useEffect)

声明State

Hook使用useState 来声明State,在使用useState 的时候,需要先引入React 的useState ,然后当你需要声明state 的时候在函数中使用useState 进行state 声明

import React,{useState} from 'react'

function useNext(){
  // 声明一个叫“李四”的state,可以通过调用newName来更新当前的name
  const [name,newName]=useState('李四')
}

注意:React 通过Hook 的调用顺序来将内部state 和Hook 进行关联,所以Hook 只能在组件的最顶层调用,如果有条件判断执行。可以放入Hook 里面进行判断

读取State

在函数中,可以直接使用state的名字来进行state读取

function useNext(){
  // 声明一个叫“李四”的state,可以通过调用newName来更新当前的name
  const [name,newName]=useState('李四')
  return(
    <p>你的名字是 {name} </p>
  )
}

更新State

在Hook 中,我们需要使用useState 中声明的setState 来进行state更新

<input type="" name="" onchange={(e) => newName(e)}/>

Hook 对于生命周期的改变

在React 中,生命周期是很重要的一部分,我们常用的生命周期函数有 componentWillMount componentDidMount componentWillUpdate componentDidUpdate componentWillUnmount 等。当我们使用Hook 的时候,Hook 提供useEffect 让你在函数组件中执行side effects

在官方文档中 side effects 被翻译为副作用,我觉得翻译为 干涉操作 可能更好一点,That process of calling into the real world is what side-effects are. https://goshakkk.name/redux-side-effect-approaches/

不需要销毁的side effects

当我们需要在Dom更新后执行一些side effects ,如请求数据,切换显示等等,不需要销毁的的操作的时候,可以使用useEffect 来进行实现,useEffect 在默认情况下,它在第一次渲染之后和每次更新之后都会执行

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

function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

}

如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可,如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

需要销毁的side effects

当组件卸载的时候,某些操作需要及时清理。防止内存泄漏的问题,我们也可以使用useEffect 来进行实现,useEffect 的设计是在同一个地方执行。如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它,

在 React class 中,你通常会在 componentDidMount 中设置订阅,并在 componentWillUnmount 中清除它。例如,假设我们有一个 ChatAPI 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态

useEffect(() => {
  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);
  };
});

Effect 还有个目的是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题,那么就需要使用到多个Effect Hook 来进行代码分离

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

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

文档参考

React 文档 https://reactjs.org/docs/hooks-intro.html
淘宝FED文章 http://taobaofed.org/blog/2018/11/27/hooks-and-function-component/