发布于 2025-12-08 2 阅读
0

使用 Hooks 开始使用 React 什么是 React?JSX 将组件拆分成多个文件 状态效果 自定义 Hooks 类组件 结论

使用 Hooks 开始使用 React

什么是 React?

JSX

将组件拆分为多个文件

状态

效果

自定义钩子

类组件

结论

你好呀。

几周前,React 正式发布了这个名为 Hooks 的新 API,而过去几个月一直好奇地关注着 React,甚至编写过一些组件的你,一直在想现在是不是该进一步了解 React 的时候了……

好吧,如果有一件事我可以向你保证,那就是现在比以往任何时候都更适合开始学习 React 技能。React 已经存在了 5 年,并从周围的环境和过去的经验中汲取了很多教训,现在它是一个非常可靠的库,几乎可以在任何地方构建应用程序。新的 API 使组件之间的逻辑组合和使用更加简洁和便捷。

让我们简单介绍一下最原始的概念,介绍最基本的钩子,然后我会给你指出一些很棒的资源,你可以在其中了解有关 React 的一切。

什么是 React?

你可能已经知道什么是 React,但我们先简单介绍一下。React 是一个库,它负责找到在某个地方渲染应用程序的最佳方式。它基于 JavaScript 构建,我们主要用它来构建 Web 应用,但如今它几乎可以用于各种平台的渲染,例如在 Android 和 iOS 上、React Native、电视、服务器、命令行等等。

这怎么可能呢?React 构建了一套逻辑,将渲染所需的最小工作量与实际的渲染原语分离开来。这样,在某个地方使用 React 时,我们只需要构建渲染层。对于 Web 来说,这就是一个react-dom库。

要创建 React 元素,我们需要做的就是将 React 和 ReactDOM 导入到 html 文件中,然后在前两个脚本标签之后打开第三个脚本标签,然后开始创建组件,对吗?

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>React Example</title>
</head>

<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react@16.8.3/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16.8.3/umd/react-dom.development.js"></script>
  <script>
    // Your code is going to go here
  </script>
</body>

</html>

嗯,还没完全搞定。我们先把根 div 添加到那里,它会是我们渲染组件的地方。不过,首先我们必须创建组件,然后才能把它们放到root我们创建的 div 里。

要创建元素,我们必须在第三个标签内执行以下操作:

const App = () => React.createElement(
    'div',
    {},
    React.createElement('h1', {}, 'My Element')
)

为了渲染它,我们在 App 组件之后添加:

ReactDOM.render(React.createElement(App), document.getElementById("root"))

现在我们应该看到h1元素内部div有“My Element”文本。

我知道,你可能会想:“真的吗?所有这些就为了渲染一些简单的 HTML 代码?” 再等等吧。

值得注意的是,React 渲染的所有内容都是组件。这是 React 的又一大优势。你的应用被拆分成多个小块,它们可以协同工作,并抽象出其内部结构。

我们在这里使用了h1div,但是您放在那里的任何内容都将是输出,因此您也可以使用 Web 组件。

我们还可以随意嵌套组件,这时事情就开始变得有趣了。

让我们创建一个Person组件。

const Person = () => React.createElement(
    'div',
    {},
    [
        React.createElement('h1', {}, 'Juliano Rafael'),
        React.createElement('h2', {}, '@frontendwizard'),
        React.createElement('h2', {}, 'Front End Developer'),
    ]
)

Person现在,我们可以在我们的应用程序中创建任意数量的组件实例:

const App = () => React.createElement(
    'div',
    {},
    [
        React.createElement(Person),
        React.createElement(Person),
        React.createElement(Person),
    ]
)

有点无聊,但很有趣。你应该在你的 html 上看到三个 Juliano。Juliano 太多了,没人会喜欢的😄。我们组件的值Person是硬编码到组件中的。让我们用最基本的组件功能来改变它,那就是props。你注意到我们一直作为第二个参数传递给 的空对象了React.createElement吗?它们就是props。它们保存了传递给对象的所有属性。方便的是,它们也是传递给函数组件的第一个参数。让我们修改Person组件以使用它。

const Person = props => React.createElement(
    'div',
    {},
    [
        React.createElement('h1', {}, props.name),
        React.createElement('h2', {}, props.twitter),
        React.createElement('h2', {}, props.job),
    ]
)

太棒了,现在我们可以摆脱那些 Juliano 的克隆并赋予每个实例自己的身份。

const App = () => React.createElement(
    'div',
    {},
    [
        React.createElement(Person, {
            name: "Juliano Rafael",
            twitter: "@frontendwizard",
            job: "Front End Developer"
        }),
        React.createElement(Person, {
            name: "Leonardo Elias",
            twitter: "@leonardoelias_",
            job: "Front End Developer"
        }),
        React.createElement(Person, {
            name: "Marcos Eptacio",
            twitter: "@eptaccio",
            job: "JavaScript Developer"
        }),
        React.createElement(Person, {
            name: "Wallace Batista",
            twitter: "@uselessdevelop",
            job: "Front End Developer"
        }),
    ]
)

很酷吧?现在我们可以征服世界,构建最复杂的应用程序了!当然,用这种语法不行。虽然我们已经可以将代码抽象成组件,但这语法太繁琐,难以阅读。是时候在此基础上进行改进了。

JSX

没有人会在不使用 JSX 的情况下使用 React,这是因为 JSX 大大提高了代码的可读性。JSX 允许你编写类似 HTML 的语法,并将其转换为我们之前一直在使用的代码。缺点是,现在我们需要在工作流程中加入这个转换过程才能使用它。我们可以@babel/standalone直接在浏览器中执行此操作,但这对生产环境不利,因为它会实时转换代码,而这是在代码被浏览器处理之前必须完成的另一个步骤。

目前 React 项目中使用的头号构建工具是webpack。得益于create-react-app,整个设置都被抽象到了 中react-scripts。你需要做的就是:

npx create-react-app my-app
cd my-app
npm start

这将启动一个全新的应用程序http://localhost:3000

现在你问我:“嘿,你是如何从带有脚本的简单 html 文件转变为使用 webpack 进行这种对我来说像魔术一样的复杂设置的?”

我懂你的意思。相信我,你一定会爱上它。现在你只需要知道,这个设置能为你提供一个可靠的工作流程来开发、调试和发布你的应用,而无需设置所有 webpack 配置,这可不是件容易的事。这个设置会将你的 JSX 代码转译成我们之前写过的代码,并额外添加一些功能,让我们能够使用 ES2015+ 中一些并非所有浏览器都支持的优秀特性。总而言之,相信它react-scripts是一个安全的选择,能让你安心无忧。

设置已经足够了,现在我们可以重构我们的Person组件以使用 JSX:

const Person = props => (
    <div>
        <h1>{props.name}</h1>
        <h2>{props.twitter}</h2>
        <h2>{props.job}</h2>
    </div>
)

App可以这样写:

const App = () => (
    <div>
        <Person
            name="Juliano Rafael"
            twitter="@frontendwizard"
            job="Front End Developer"
        />
        <Person
            name="Leonardo Elias"
            twitter="@leonardoelias_"
            job="Front End Developer"
        />
        <Person
            name="Marcos Eptacio"
            twitter="@eptaccio"
            job="JavaScript Developer"
        />
        <Person
            name="Wallace Batista"
            twitter="@uselessdevelop"
            job="Front End Developer"
        />
    </div>
)

我不知道你是怎么想的,但这对我来说感觉好多了。组件嵌套得越多,你就越能看到 JSX 的好处。

不过,JSX 还是有一些小问题,你可以在这里的文档中阅读 JSX 的所有细节。目前,你需要知道的是:

  • 您的组件(除 HTML 中指定的组件外)必须大写,否则它会尝试将其作为 Web 组件而不是 React 组件。
  • 如果您要向组件添加一个类,则需要使用className属性,而不是class因为 class 是 JavaScript 上的保留字。
  • 所有带有连字符的属性都需要加上前缀,data-否则 React 将不会在 DOM 上呈现它们。

我可以接受这一点。

将组件拆分为多个文件

您可能已经注意到,create-react-app默认情况下,组件会被拆分成单独的文件,并使用 ES2015 语法进行导入和导出。

这得益于后台的 webpack 设置。有了它,你可以将每个组件放入它自己的文件中,然后使用以下命令将其导出:

export default MyComponent

在另一个文件中,您可以使用 import 语句导入组件:

import MyComponent from './relative-path-to-MyComponent'

好的,现在我们可以用类似 HTML 的语法编写组件,将其分解成更小的组件,抽象出它的实现,但所有这些仍然是静态的。这是因为 props 在组件内部无法更改,它们是不可变的。我们需要一个可以存储状态并动态更改状态的东西,同时让 React 更新界面作为响应。

让我们谈论状态。

状态

状态是组件的一个动态属性。你可以在组件的生命周期内随意更改它,React 会负责更新渲染的组件,以反映状态的变化。每次你更改状态时,React 都会找到最快的方式更新渲染的组件,并负责为你完成更新。这真是太棒了。

为了创建状态,我们将使用 Hook。Hook 是 React 16.8 的新增功能,它极大地简化了 React API,并允许以全新的方式来构建和组合组件及功能。可以肯定的是,从现在开始,Hook 将成为我们创建组件的默认方式。

我们可以使用useStatefromReact并传入 initialValue 作为第一个参数来创建一个状态。此钩子将返回一个数组,其中第一个值是当前状态,第二个值是用于更新状态的函数。

const Counter = () => {
    const [count, setCount] = React.useState(0)
    const handleClick = () => setCount(count + 1)
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={handleClick}>Click Me!</button>
        </div>
    )
}

这是我们第一个真正的动态组件🎉🎉🎉。这正是 React 的真正优势所在。创建动态组件非常简单。

但事情可能会变得更有趣。只是useState我们实际上无法实现异步操作,而且说实话,Web 开发的主要部分是与 API 交互。那么,我们该怎么做呢?

效果

有时你需要对变化做出响应。这就是我们所说的 effect。你可以为组件生命周期内的各种事件触发 effect,例如挂载、卸载和更新。

为了演示这个概念,我们来做一些更贴近现实的事情。我们创建一个组件,从这个公共 API获取一张随机的猫咪图片,并将其渲染到页面上。

对于效果,我们有另一个钩子,即useEffect。这个钩子接受两个参数:第一个是在组件生命周期的某个时刻执行的函数,第二个是一个可选数组,列出了此函数所依赖的内容。第二个参数可以实现三件事:

  • 如果没有传递任何内容给它,React 会假定无论何时发生任何事情,都需要再次运行此效果。
  • 如果传递一个空数组,React 将假定该函数只需要在组件第一次渲染后运行一次。
  • 如果你在这个数组中放入一些东西,那么每次数组内的任何元素发生变化时,React 都会运行效果。

就这样:

const RandomCat = () => {
  const [ready, setReady] = React.useState(false)
  const [src, setSrc] = React.useState(null)
  const [error, setError] = React.useState(null)
  React.useEffect(() => {
    fetch("https://aws.random.cat/meow")
      .then(response => response.json())
      .then(data => {
        setReady(true)
        setSrc(data.file)
      })
      .catch(error => setError(error))
  }, [])
  if (!ready) return <p>Loading...</p>
  if (error) return <p>Oops, something went wrong!</p>
  return <img src={src} alt="random cat photo" />
}

瞧,我们的组件向第三方 API 发出了请求,获取了图片地址,并用图片标签渲染了它。我们可以将其重构为自定义钩子,使其更加完善。

自定义钩子

我们不必局限于使用 React 提供的 hooks。我们可以创建自己的 hooks,抽象出细节,只暴露重要的部分。让我们开始动手吧:

const useRandomCatImg = () => {
  const [ready, setReady] = React.useState(false)
  const [src, setSrc] = React.useState(null)
  const [error, setError] = React.useState(null)
  React.useEffect(() => {
    fetch("https://aws.random.cat/meow")
      .then(response => response.json())
      .then(data => {
        setReady(true)
        setSrc(data.file)
      })
      .catch(error => setError(error))
  }, [])
  return { ready, error, src }
}

const RandomCat = () => {
  const { ready, error, src } = useRandomCatImg()
  if (error) return <p>Oops, something went wrong!</p>
  if (!ready) return <p>Loading...</p>
  return <img src={src} alt="random cat photo" />
}

这样做的直接好处是,我们现在可以useRandomCatImg在任何我们认为合适的地方使用它。

这里有几点需要注意:

  • 钩子必须在函数组件内部调用
  • 这是一个约定,Hooks 应该始终以 开头use。这是因为 React 会假定你遵循了这一约定,并且只有在你遵循此约定时才会警告你违反了约定。所以,一定要遵循!

另外,请求过程中的状态管理确实有点繁琐和笨拙,但请稍等,因为 React Suspense 很快就会解决这个问题。我现在不打算深入讲解,因为我们还没讲到那一步,但你应该知道,它很快就会变得更好。

类组件

尽管 Hooks 很棒,但它最近才正式推出,而在此之前,React 的所有强大功能都已经存在,只是方式不同。虽然我们肯定会迁移到 Hooks 作为标准,但类组件仍然存在,而且它们不会消失。此外,在 Hooks 出现之前,你很可能会在任何地方找到它们。

通过创建一个类并扩展,你可以创建一个类组件React.Component。这样,你就可以访问组件的生命周期方法,例如componentDidMountcomponentDidUpdate等。你还可以实例化并使用它进行更改。componentShouldUpdatecomponentWillUnmountstatethis.setState

您可以使用类组件执行的所有操作,都可以使用钩子执行,反之亦然,除了componentDidCatch生命周期方法之外,但这也适用于钩子。

我们之前用钩子做的相同RandomCat组件,带有类,如下所示:

class RandomCat extends React.Component {
  constructor(props) {
    super(props)
    this.state = {}
    this.fetchRandomCatImg = this.fetchRandomCatImg.bind(this)
  }

  componentDidMount() {
    this.fetchRandomCatImg()
  }

  fetchRandomCatImg() {
    fetch("https://aws.random.cat/meow")
      .then(response => response.json())
      .then(data => {
        this.setState({
          error: null,
          ready: true,
          src: data.file
        })
      })
      .catch(error => this.setState({ error }))
  }

  render() {
    if (this.state.error) return <p>Oops, something went wrong!</p>
    if (!this.state.ready) return <p>Loading...</p>
    return <img src={this.state.src} alt="random cat photo" />
  }
}

虽然代码更冗长,但功能相同。由于使用了 ,这段逻辑fetchRandomCatImg无法在其他地方共享,因此复用起来也更加困难this,这使得它依赖于组件实例。这个问题有一些解决方案,主要是渲染 props 和高阶组件模式,但它们也各自带来了一些问题。Hooks 只是在 React 应用中实现逻辑复用的一种更好的方法。

与往常一样,Dan Abramov发表了一篇精彩的文章,解释了为什么钩子如此好。

结论

状态和效果是 React 的核心。它们占了 React 的 20%,却完成了 80% 的事情。你的下一步应该如下:

  • 如果你还没看过,可以去看看文档。React文档非常棒,目前正在翻译成多种语言。如果你的语言版本尚未完成,不妨尝试贡献一份力量。所有版本文档的 repos 都可以在这里找到。
  • 学习如何使用路由器。我推荐你研究一下Ryan Florence开发的Reach Router,因为它注重易用性。
  • 学习如何处理表单,特别是受控组件。React 确实有另一种处理表单输入的方式,一开始可能会感觉有点别扭。帮自己一个忙,使用formik 吧
  • 学习如何测试 React 组件。Kent C. Dodds有很多关于这个主题的高质量内容,他也是react-testing-library的作者,这是一款非常棒的组件测试工具。我再怎么强调测试的重要性也不为过。他还有一篇介绍这个库的文章。可以去看看。
  • 看看一些状态管理解决方案。Redux最常用的,但一开始可能会让人不知所措,而且它并非唯一的选择。MobX以简洁著称。状态管理的一个好规则是,只在真正需要的时候才使用它。很多时候,它足以在树上的远距离组件之间共享状态。不过Context一开始你完全不需要担心这个问题。
  • 看看 React 生态系统中的 CSS 工具。我非常喜欢 CSS-in-JS 库,因为它在编写样式时赋予你更强大的功能,并且与 React 完美契合,可以将你的样式转化为组件。我非常喜欢styled-components,但Emotion也是一个不错的选择。如果你正在寻找更有说服力的 CSS-in-JS 论据, Max Stoiber最近的文章《我为什么用 JavaScript 编写 CSS》值得一读。

目前来说,这些已经足够了,肯定超出了我写这篇文章的预期😄。不要因为我刚才放了这么多链接而泄气。慢慢来,根据自己的时间尝试每个主题。更重要的是,多联系其他人,提出问题。React 社区非常乐于助人,你可以在任何地方找到帮助(reddit、Twitter 上的开发者、freeCodeCamp以及许多其他平台)。

最后,如果你还在看,请在下方评论区留言,告诉我你学习 React 的历程。我很想听听你的故事。另外,点击关注按钮,就能在我的下一篇文章发布时收到通知。

今天就到这里啦。✌️

封面图片由 Caspar Camille Rubin 在 Unsplash 上提供

鏂囩珷鏉ユ簮锛�https://dev.to/frontendwizard/starting-out-with-react-on-2019-3a0c