import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App'; // 嘿,看这里,这里变化了
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
[译]使用 React Hooks 构建电影搜索应用程序
前言:
在这篇文章中,我们将使用 React Hooks 构建一个非常简单的应用程序。因此,我们不会在此应用程序中使用任何class 组件。 我将解释一些API的工作原理,以便于使你能在构建其它应用程序时能更得心应手地使用 React Hooks。
以下是完成这个应用程序之后的页面截图:
我知道,这名字看起来很有创造性...
基本上,该程序可通过 OMDB API 来搜索电影并将结果返回给我们。构建此应用程序的目的在于使我们更加理解 React Hooks 并且助你在自己开发的项目中更好地使用它,那么,我们开始吧!在此之前,你需要做一些事情:
开始构建
创建 React app
这个教程将会使用 react 脚手架工具 create-react-app 来构建我们的应用,如果你还没有安装这个脚手架工具,在终端执行以下命令:
接下来,创建我们的 React app,在终端输入以下命令:
完成后,我们应该有一个名为 “Hooked” 的文件夹,其目录结构如下所示:
初始化的项目结构
创建所需组件
此应用程序中包含4个组件,我来概述下每个组件及其功能:
让我们开始创建它们吧,在
src
目录下,创建一个新文件夹命名为components
,这个文件夹存放我们所有的组件,将App.js
文件拖进去。接着,我们创建一个新的文件命名为Header.js
,并输入以下代码:这个组件不需要太多的解释,就是一个很基本的组件,接受
props
,并将props.text
渲染为页面标题。别忘记更新我们的
index.js
文件:现在你执行
npm run start
必然是成功不了的,一是我们App.js
路径变了,引入App.css
的路径也没改,而且许多默认构建的元素如logo.svg
路径都没变,我们现在先不急,等我们把组件都写好之后,在集中修改App.js
。接下来在
components
下继续创建一个新的组件Movie.js
,添加以下代码:这就需要解释一下啦~ 但它也只是一个无状态组件(没有任何内部状态),用于呈现电影的标题,图像和年份。之所以使用
DEFAULT_PLACEHOLDER_IMAGE
,是因为从 API 检索的某些电影没有图片,因此我们以一个自己预设好的图片作为替换,而不是一个断开的链接,这对用户很不友好。现在我们来创建组件
Search.js
,这部分很令人激动,因为在过去,为了处理内部状态,我们不得不创建一个 class 组件... 但是现在不用了!因为有了 hooks ,我们现在可以创建一个普通的函数就能处理内部状态,就问你厉不厉害。在文件夹components
下创建文件Search.js
,添加以下代码:这真的太酷了,你不用像以前一样在 class 组件中的 constructor 中创建状态,利用 setState 更新状态,以及繁琐的 .bind(this) 。我相信你已经看过了我们使用的
useState
,顾名思义,它使我们可以将 React 状态添加到普通函数组件中。useState
接受一个参数,该参数是初始状态,然后返回一个包含当前状态(等同于类组件的this.state
)和更新它的函数(等同于this.setState
)的数组。在本例中,我们将当前状态作为搜索输入字段的值。 因为注册了
onChange
事件,在输入改变时,将调用handleSearchInputChanges
函数,该函数使用新的输入值去更新当前状态。resetInputField
函数就是重置输入框的值为空字符串。 点我了解更多useState
API 信息。最后,我们来解决我们之前留下的坑,更新
App.js
:让我仔细研究下上面的代码:我们使用了3个
useState
函数,是的,我们可以在一个组件中写多个useState
函数,第一个用于处理加载状态(将loading设置为true时,它会呈现“ loading…”文本)。第二个用于处理从服务器获取的电影数组。 第三个用于处理发出API请求时可能发生的任何错误。之后,我们遇到了应用程序中使用的第二个钩子 API:
useEffect
钩子。 该钩子可以在功能组件中执行副作用。 所谓副作用,是指诸如数据获取,订阅和手动 DOM 操作之类的事情。 关于这个钩子的最好的部分是 React 官方文档中的这句话:其实就是说,
useEffect
在首次渲染(componentDidMount)以及之后每次更新(componentDidUpdate)都被调用。我知道你可能想知道如果每次更新后都调用它,那与 componentDidMount 有何相似之处呢? Emmm..,这是因为
useEffect
函数接受两个参数,一个是你要运行的函数,另一个是数组,你仔细看看官方文档的代码或上面我们自己写的代码。 在该数组中,我们只传入一个值,该值告诉 React 如果传入的值没有被更改,则跳过此次调用。根据文档,这类似于我们在 componentDidUpdate 中添加条件语句时的情况:
在我们的例子中,我们没有任何变化的值,因此我们可以传入一个空数组,该数组告诉 React 这个效果应该被调用一次。
如你所见,我们有3个
useState
函数,它们看起来有相关性,应该有可能将它们组合在一起。 为了做到这点,React 团队已经为我们想到了,于是他们制作了一个有助于此操作的钩子 - 该钩子称为useReducer
。 让我们将App组件转换为使用useReducer
的新组件,这样我们的App.js
现在将如下所示:如果一切顺利,那么我们应该不会看到应用程序与之前相比有任何变化。 现在让我们来看一下
useReducer
挂钩的工作方式。该 hook 接受3个参数,但在我们的用例中,我们将仅使用2个。典型的
useReducer
钩子如下所示:reducer
参数类似于我们在 Redux 中使用的参数,看起来像这样:reducer
接收initialState
和action
,因此reducer
根据action.type
返回一个新的状态对象。 例如,如果调度的操作类型为SEARCH_MOVIES_REQUEST
,则状态将使用新对象更新,其中loading
的值为 true ,而errorMessage
为 null。值得一提的是,在搜索功能中,我们实际上是在分派三个不同的动作:
SEARCH_MOVIES_REQUEST
动作,它更新我们的状态对象,使loading = true
且errorMessage = null
。SEARCH_MOVIES_SUCCESS
的动作,该动作将更新我们的状态对象,从而使loading = false
和movie = action.payload
,其中payload
是从OMDB获得的movie 数组。SEARCH_MOVIES_FAILURE
的其他操作,该操作更新状态对象,使loading = false
和errorMessage = action.error
,其中action.error
是从服务器获取的错误消息。要了解有关
useReducer
钩子的更多信息,请查看官方文档。最后修改我们的
App.css
(这部分不是重点,直接把现在所需的样式全给你们了,作为参考):你做到了!
哇!!! 我们已经走了很长一段路,我相信你对 hooks 的可能性感到兴奋。 就我个人而言,将初学者介绍给 React 非常容易,因为我不再需要解释
class
的工作方式或this
的工作方式,或者在JS中bind
的工作方式。在本教程中,我们仅涉及了一些钩子,甚至没有介绍创建自己的自定义钩子等功能。 如果您还有其他用钩的用例,或者已经实现了自己的自定义钩,请添加评论并加入其中。
这篇文章的代码就不提供了,我希望任何能安心看下来的小伙伴能手动敲一边,收获还是有的!~
后记:
因为笔者也是刚刚学 React hooks,在掘金上另一篇文章中看到了推荐这个项目,自己读了一遍,做了一遍,发现作为入门还是不错的,故想翻译一下让更多学习 React hooks 的小伙伴能学习到~若是翻译有误,还请指正,谢谢啦🙏。
这篇文章收录于我自己的Github/blog,若对你有所帮助,欢迎 star,之后会陆续推出更多基础优质文章~