React Hooks (Proposal)

原文连接:Githubhtml

React Hooks (Proposal)

在 React v16.7.0 alpha 版本里,提出了一个新的 Feature Proposal :Hooks ,对社区以及之后前端发展所带来的影响是巨大的。前端

学习 Hooks 的知识须要对 React 生态有较深刻的理解vue

What is the Hooks ?

Hooks 是 React 内部组件中的一系列特殊函数,直观带来的改变是引入state、生命周期函数、或者其余 React 功能,无需使用 classes 编写组件(类语法带来的问题有不少),背后为前端带来更深刻更普及的 functional programming 思想。react

引入 Hooks 的动机

React 官方阐明了引入 Hooks 的动机,Hooks 出现前,咱们编写 React 组件 会常常遇到的问题:git

  1. It’s hard to reuse stateful logic between components
    • React 没有提供官方方案去解决 组件之间共享复用有状态逻辑 ,组件间逻辑的复用和数据传递就变得十分困难(必须一层一层往下传),因此咱们使用 render propshigher-order components 来解决复用逻辑的同时引来了新的问题,一些无关 UI 的 wrapper 组件愈来愈多,嵌套组件愈来愈深,造成 wrapper hell ,虽然 React devTools 有过滤器来帮助咱们更容易地调试。
    • 使用 Hooks 能够在不改变组件层次结构的状况下复用有状态逻辑。能够利用 custom hooks,复用包含状态的逻辑,这些逻辑再也不出如今组件树中,而是造成一个独立、可测试的单元,但仍然响应 React 在渲染之间的变化;社区之间分享 自定义hooks 更容易,hooks 就像插件同样。
  2. Complex components become hard to understand
    • 随着项目深刻,咱们逐渐会编写愈来愈复杂的逻辑在组件中,这致使了再生命周期函数内编写的逻辑很是臃肿,例如 添加监听器,咱们须要在componentDidMountcomponentWillUnmount 中分别编写添加与删除监听器的逻辑,而通常在 componentDidMount 中,咱们也会编写 请求数据 的逻辑。各类功能不相关联的逻辑写在一块儿,并且相同功能的逻辑散落在不一样函数内,这带来许多隐患以及调试上的困难
    • 使用 Hooks 能够 将相关联的逻辑code由组件拆分出来成更简单直观的函数(例如订阅事件、请求数据)
  3. Classes confuse both people and machines
    • React 官方认为 JS 的 Class 语法的学习成本很高,使用类语法,要必须清楚 this 在 JS 的工做方式,例如咱们须要 绑定事件处理程序 (以何种方式绑定这里不是重点,我的推荐箭头函数形式);另一些重要实践上,使用 Class 语法也带来诸多问题,详细参阅 classes-confuse-both-people-and-machines)
    • 使用 Hooks 能够 在无需编写 Class 语法的状况下 引入state、生命周期函数、或者其余 React 功能

实际上引入 Hooks 并不会给现有的代码带来问题

  1. 彻底可选(将使用 Hooks 的选择权交给开发者)
  2. 向后兼容(不会有任何破坏性更改)
  3. 在可预见的将来内,不会从 React 中删除 类语法
  4. Hooks 并无颠覆以前的 React 概念。相反,带来更直观的 API 实现相同的功能

编写 Hooks

目前主要的 Hooks :github

  1. State hooks
  2. Effect hooks
  3. Custom hooks (自定义 hooks 用来复用包含状态的逻辑)

useState

import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

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

使用 state hooksfunction components中能够像上面代码这样,等同于Class语法的代码就不贴了。数组

值得一提的是,在 Hooks 出现以前,咱们一般叫这样形式的组件为 stateless components or stateless function components ,但如今,有了 Hooks ,咱们能够在这类组件中使用 state,因此改称 function components闭包

  1. useState 的参数是 咱们须要定义的 state 名的初始值(没必要像之前同样,state 必须为 Object,若是咱们想要建立两个state,就调用两次 useState)
  2. 返回值是包含两个值的数组,两个值分别为 当前状态更新它的函数 。(这里咱们使用 array destructuring 的方式将值取出来。)

建立多个 state 就像这样app

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
复制代码

this.setState 不一样,更新状态老是替换它而不是合并它(也解决了不少以前合并带来的问题)less

Functional updates

若是新的 state 值是依赖上一个 state 值来计算的,咱们能够给 setState 传递一个函数参数,这个函数的参数为上一个 state 的值,返回值是更新后的 state 值,例如:

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <> Count: {count} <button onClick={() => setCount(0)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); } 复制代码

因此若是须要更新的 state 值为 Object,咱们应该使用 object spread syntax

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});
复制代码

延迟初始化 state

若是初始化的值是须要大量计算获得的结果,可使用函数代替,此函数只会在初始化阶段执行

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});
复制代码

useEffect

Effect 其实就是 请求数据,操做DOM,以及订阅事件等一系列 反作用/效果

而 useEffect 则是 以前 componentDidMountcomponentDidUpdatecomponentWillUnmount 的结合

React组件中有两种常见的 Effect:须要清理和不须要清理的 Effect

不须要清理的 Effect

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
  );
}
复制代码
  1. 将在每次渲染后执行 useEffect
  2. useEffect 写在 函数内部是为了直接访问到state值,利用了闭包的性质,不须要额外 API

须要清理的 Effect

import { 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);
    // Specify how to clean up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

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

须要单独的 API 来执行清理逻辑。由于添加和删除订阅的逻辑是相关的,useEffect 旨在将其保持在一块儿。 若是 useEffect 返回一个函数,React 将在清理时执行它

清理的时机是 当组件卸载时,但,useEffect 会在每次渲染后运行而不只仅是一次, 这就是 React 在下次执行 useEffect 以前还清除前一个 useEffect 的缘由;Using the Effect Hook – React

若是要减小 useEffect 内并非每次渲染都必要的逻辑,能够:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
复制代码

React 会比较两次渲染的 count 值,若是同样,就会跳过此次 useEffect

Custom Hooks

咱们能够封装在多个组件可重用的包含状态的逻辑,例如

import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

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

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}
复制代码

useFriendStatus 就是一个咱们写好的复用逻辑函数,供其余组件调用。

多个组件使用 相同自定义Hooks,它们的状态和效果是 独立隔离的,仅仅是逻辑的复用。由于本质是 调用 Custom Hooks 是调用 useStateuseEffect,它们在一个组件调用不少次,彼此产生的状态也是彻底独立的。

详细参见文档:Writing Custom Hooks – React

使用 Hooks 的规则:

务必遵照的规则: Rules of Hooks – React

Hooks API: Hooks API Reference – React

Conclusion

React Hooks 带来的边际效应能够说是巨大的,但愿更加完善以后,能够看到打开新窗的前端。

启发 Hooks 的产生:Hooks FAQ – React

关于 Hooks 的讨论:RFC: React Hooks by sebmarkbage · Pull Request #68 · reactjs/rfcs · GitHub

有趣的是,Vue的做者也很快建立了在 Vue 实验 Hooks 的repo:GitHub - yyx990803/vue-hooks: Experimental React hooks implementation in Vue

原文连接:Github

相关文章
相关标签/搜索