资讯专栏INFORMATION COLUMN

React Hooks 入门(2019)

GitCafe / 2039人阅读

摘要:到目前为止,表达这种流程的基本形式是课程。按钮依次响应并更改获取更新的文本。事实证明不能从返回一个。可以在组件中使用本地状态,而无需使用类。替换了提供统一,和。另一方面,跟踪中的状态变化确实很难。

备注:为了保证的可读性,本文采用意译而非直译。

在这个 React钩子 教程中,你将学习如何使用 React钩子,它们是什么,以及我们为什么这样做!

主要内容

你将学到什么

要求

项目设置

一开始有 setState

更新 React 中的状态,没有 setState

开始时有 componentDidMount(和 render props)

使用 useEffect 获取数据

可以使用 React 钩子渲染吗?

第一个自定义 React 钩子

可以使用 async / await 和 useEffect 吗?

结语

附录

1、你将学到什么

如何使用 React 钩子

如何在 React 类组件中实现相同的逻辑

. 2、要求

学习以下内容,你应该基本了解

ES6(箭头函数、解构、类)

React

3、项目设置

这里默认你已经配置好 React 开发环境,我们试着安装

npx create-react-app exploring-hooks
4、一开始有 setState

假设你已经在你的项目中使用了 React,让我们快速回顾一下:

React 是一个用于构建用户界面的库,其优点之一是库本身会向开发人员强加严格的数据流。你还记得 jQuery 吗?使用 jQuery,不太可能清楚地构建项目,更不用说定义数据应如何在 UI 中展示。这很难跟踪哪些功能正在改变哪个 UI 视图。

这同样适用于普通的 JavaScript:即使有自我解释和实践,也可以提出一个结构良好的项目(考虑模块模式),好的跟踪状态和功能之间的相互作用(请参阅 Redux )。

React 在某种程度上缓解了什么问题呢:通过强制执行清晰的结构(容器 和 功能组件) 和 严格的数据流(组件对状态 和 道具更改 做出反应),现在比以前更容易创建合理的 UI 视图逻辑。

因此,React 理论上是,一个 UI 可以 “ 响应 ” 以响应状态变化。到目前为止,表达这种流程的基本形式是 ES6 课程。考虑以下示例:从React.Component 扩展的 ES6 类,具有内部状态:

import React, { Component } from "react"
export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" }
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" }
    });
  }
  render() {
    const { buttonText } = this.state
    return 
  }
}

从上面的代码中可以看出,当单击按钮时,组件的内部状态会被 setState 改变。按钮依次响应并更改获取更新的文本。

由于 类字段,删除构造函数可以表示更简洁的组件版本:

import React, { Component } from "react"
export default class Button extends Component {
  state = { buttonText: "Click me, please" }
  handleClick = () => {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" }
    })
  };
  render() {
    const { buttonText } = this.state
    return 
  }
}
5、更新 React 中的状态,没有 setState

那么我们现在有什么选择来管理 React 中的内部状态,是因为不再需要 setState 和 类 了吗?
第一个也是最重要的 React 钩子:useState。useState 是 react 暴露的函数。将在文件顶部引入它:

import React, { useState } from "react"

通过在代码中导入 useState,在 React 组件中保存某种状态的意图。更重要的是,React 组件不再是 ES6 类。它可以是一个纯粹而简单的 JavaScript 函数。
导入 useState 后,将选择一个包含两个变量的数组,这些变量不在 useState 中,代码应该放在 React 组件中:

const [buttonText, setButtonText] = useState("Click me, please")

如果对这种语法感到困惑,其实这是 ES6 解构。上面的名字可以是你想要的任何东西,对 React 来说无关紧要。

所以前面的例子,一个带有钩子的按钮组件会变成:

import React, { useState } from "react"
export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please")
  return (
    
  )
}

要在 onClick 处理程序中调用 setButtonText 状态更新程序,可以使用箭头函数。但如果你更喜欢使用常见的功能,你可以:

import React, { useState } from "react"
export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please")
  function handleClick() {
    return setButtonText("Thanks, been clicked!")
  }
  return 
}

除了有特殊要求外,我更喜欢常规功能而不是箭头函数的方式。可读性提高了很多。此外,当我编写代码时,我总是认为下一个开发人员将保留代码。这里的代码应该是可读更强的。

6、开始时有 componentDidMount(和 render props)

在 React 中获取数据!你还记得 componentDidMount 吗?你可以在 componentDidMount 中点击提取(url)。以下是如何从 API 获取数据以及如何展示为一个列表:

import React, { Component } from "react"
export default class DataLoader extends Component {
  state = { data: [] }
  componentDidMount() {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data =>
        this.setState(() => {
          return { data }
        })
      )
  }
  render() {
    return (
      
    {this.state.data.map(el => (
  • {el.title}
  • ))}
) } }

你甚至可以在 componentDidMount 中使用 async / await,但有一些注意事项。但是在项目中的大多数的异步逻辑都存在 React 组件之外。上面的代码还有一些缺点。
渲染列表他是固定的,但使用渲染道具,我们可以轻松地将子项作为函数传递。重构的组件如下所示:

import React, { Component } from "react"
export default class DataLoader extends Component {
  state = { data: [] }
  componentDidMount() {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data =>
        this.setState(() => {
          return { data }
        })
      )
  }
  render() {
    return this.props.render(this.state.data)
  }
}

并且你将通过从外部提供渲染道具来使用当前该组件:

 {
    return (
      
    {data.map(el => (
  • {el.title}
  • ))}
) }} />
7、使用 useEffect 获取数据

我认为使用 React 钩子获取数据不应该与 useState 有什么不同。工作中看文档给了我一个提示:useEffect 可能是正确的一个工具。
当看到:“ useEffect 与 React 类中的 componentDidMount,componentDidUpdate 和 componentWillUnmount 具有相同的用途时,统一为单个 API ”。
而且我可以使用 setData(从 useState 中提取的任意函数)代替调用 this.setState:

import React, { useState, useEffect } from "react"
export default function DataLoader() {
  const [data, setData] = useState([])
  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  });
  return (
    
    {data.map(el => (
  • {el.title}
  • ))}
) }

在这一点上,我想“ 可能出现什么问题? ”,这是在控制台中看到的:

这显然是我的错,因为我已经知道发生了什么:
“ useEffect 与 componentDidMount,componentDidUpdate 和 componentWillUnmount 具有相同的用途
componentDidUpdate!componentDidUpdate 是一个生命周期方法,每当组件获得新的道具或状态发生变化时运行。
这就是诀窍。如果你像我一样调用 useEffect,你会看到无限循环。要解决这个“ bug ”,你需要传递一个空数组作为 useEffect 的第二个参数:

//
  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data))
  }, []); // << super important array
//
8、可以使用 React 钩子渲染吗?

当然!但是这样做没有意义。我们的 DataLoader 组件将会变为:

import React, { useState, useEffect } from "react"
export default function DataLoader(props) {
  const [data, setData] = useState([])
  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data))
  }, []); // << super important array
  return props.render(data)
}

并且你将通过从外部提供渲染道具来消耗组件,就像我们在前面的示例中所做的那样。
但同样,这种重构没有意义,因为 React 钩子的诞生是有原因的:在组件之间共享逻辑,我们将在下一节中看到一个例子。

9、第一个自定义 React 钩子

我们可以将我们的逻辑封装在 React 钩子中,然后在我们需要引入该钩子时,而不是 HO C和 渲染道具。在我们的示例中,我们可以创建用于获取数据的自定义挂钩。
根据 React 文档,自定义钩子是一个 JavaScript 函数,其名称以“ use ”开头。比说起来容易。让我们创建一个 useFetch :

// useFetch.js
import { useState, useEffect } from "react"
export default function useFetch(url) {
  const [data, setData] = useState([])
  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data))
  }, []);
  return data
}

这就是你如何使用自定义钩子:

import React from "react"
import useFetch from "./useFetch"
export default function DataLoader(props) {
  const data = useFetch("http://localhost:3001/links/")
  return (
    
    {data.map(el => (
  • {el.title}
  • ))}
) }

这就是使钩子如此吸引人的原因:最后我们有一个 有趣的、标准化的、干净 的方式来封装和共享逻辑。

10、我可以使用 useEffect 的 async / await 吗?

当你在使用 useEffect 时想在钩子里尝试 async / await。让我们看看我们的自定义:

// useFetch.js
import { useState, useEffect } from "react"
export default function useFetch(url) {
  const [data, setData] = useState([])
  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data))
  }, [])
  return data
}

对于 重构异步 / 等待 最自然的事情你可能会:

// useFetch.js
import { useState, useEffect } from "react"
export default function useFetch(url) {
  const [data, setData] = useState([])
  useEffect(async () => {
    const response = await fetch(url)
    const data = await response.json()
    setData(data)
  }, [])
  return data
}

然后打开控制台,React 正在提示着:

“警告:不能返回任何内容。

事实证明不能从 useEffect 返回一个 Promise。JavaScript 异步函数总是返回一个 promise,而 useEffect 应该只返回另一个函数,该函数用于清除效果。也就是说,如果你要在 useEffect 中启动 setInterval,你将返回一个函数(我们有一个闭包)来清除间隔。

因此,为了使 React,我们可以像这样重写我们的异步逻辑:

// useFetch.js
import { useState, useEffect } from "react"
export default function useFetch(url) {
  const [data, setData] = useState([])
  async function getData() {
    const response = await fetch(url)
    const data = await response.json()
    setData(data)
  }
  useEffect(() => {
    getData()
  }, [])
  return data
}

这里,你的自定义钩子将再次运行。

11、结语

React hooks 是库的一个很好补充。他们于 2018年10月 作为 RFC 发布,很快就赶上了 React 16.8。将 React 钩子想象为生活在 React 组件之外的封装状态。

React 钩子使渲染道具和 HOC 几乎过时,并为共享逻辑提供了更好的人体工程学。使用 React 钩子,你可以在 React 组件之间重用常见的逻辑片段。

React 附带一堆预定义的钩子。最重要的是 useState 和 useEffect。useState 可以在 React 组件中使用本地状态,而无需使用 ES6 类。

useEffec t替换了提供统一 API:componentDidMount,componentDidUpdate 和 componentWillUnmount。还有很多其他的钩子,我建议阅读官方文档以了解更多信息。

很容易预见React的发展方向:功能组件遍布各处!但即便如此,我们还是有三种方法可以在 React 中表达组件:

功能组件

类组件

带有挂钩的功能组件

12、附录

在文章的开头我说:“ 使用 jQuery,几乎不可能清楚地构建项目,更不用说定义数据应如何在 UI 中展示”。

但是你可能不需要 React 来构建用户界面。有时我使用 vanilla JavaScript 构建项目。当我不确定该项目将采用什么形状时,我用来创建一个没有任何 JavaScript 库的简单原型。

在这些项目中,我依靠模块的方式来编写代码。

能够正确组织和编写你的代码,即使使用 vanilla JavaScript 也是每个 JavaScript 开发人员的宝贵资产。为了更多地了解 JavaScript 中的模块方式,我建议你由 Todd Motto 掌握模块方式和 Addy Osmani 的 JavaScript 设计模式。

另一方面,跟踪 UI 中的状态变化确实很难。对于这种工作,我最喜欢的是 Redux,甚至可以使用 vanilla JavaScript。

备注:原文链接地址 https://www.valentinog.com/bl...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105406.html

相关文章

  • React Hooks入门到上手

    摘要:前言楼主最近在整理的一些资料,为项目重构作准备,下午整理成了这篇文章。给传入的是一个初始值,比如,这个按钮的最初要显示的是。取代了提供了一个统一的。 showImg(https://segmentfault.com/img/bVbpUle?w=900&h=550); Hooks are a new addition in React 16.8. They let you use sta...

    XFLY 评论0 收藏0
  • React Hooks入门到上手

    摘要:前言楼主最近在整理的一些资料,为项目重构作准备,下午整理成了这篇文章。给传入的是一个初始值,比如,这个按钮的最初要显示的是。取代了提供了一个统一的。 showImg(https://segmentfault.com/img/bVbpUle?w=900&h=550); Hooks are a new addition in React 16.8. They let you use sta...

    zhouzhou 评论0 收藏0
  • 前端小报 - 201902 月刊

    摘要:也可以安装浏览器插件或,在某个文件的网页上就能看到一个的按钮,点击即可。 【AIS-TXD前端技术月刊】- 本月热门前端技术快报,汇聚 Github Trending 流行 Repo 和热门文章,文末有福利 showImg(https://segmentfault.com/img/remote/1460000018406928?w=1352&h=808); 欢迎 订阅 & 投稿本期小编...

    Dean 评论0 收藏0
  • 前端小报 - 201902 月刊

    摘要:也可以安装浏览器插件或,在某个文件的网页上就能看到一个的按钮,点击即可。 【AIS-TXD前端技术月刊】- 本月热门前端技术快报,汇聚 Github Trending 流行 Repo 和热门文章,文末有福利 showImg(https://segmentfault.com/img/remote/1460000018406928?w=1352&h=808); 欢迎 订阅 & 投稿本期小编...

    zombieda 评论0 收藏0
  • 前端小报 - 201902 月刊

    摘要:也可以安装浏览器插件或,在某个文件的网页上就能看到一个的按钮,点击即可。 【AIS-TXD前端技术月刊】- 本月热门前端技术快报,汇聚 Github Trending 流行 Repo 和热门文章,文末有福利 showImg(https://segmentfault.com/img/remote/1460000018406928?w=1352&h=808); 欢迎 订阅 & 投稿本期小编...

    HitenDev 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<