通过构建表情包生成器来学习 React
表情包很棒——它们是表达想法和观点的一种非常有趣的方式。因此,我选择表情包生成器应用作为我在Scrimba 上的免费 React 课程的毕业设计项目也就不足为奇了。这款应用的工作原理是从 API 获取一张随机的表情包图片,然后将你的文字叠加在上面,从而创建出你专属的个性化表情包。
所以在本文中,我将提供创建应用程序的分步指南。如果您遇到任何困惑,也可以参考 Scrimba 课程中的这些步骤,从本讲开始。
如果您喜欢我的教学风格,并且在完成本教程后想要迎接更艰巨的挑战,请查看我即将推出的Scrimba 高级课程。
注意:你应该已经对 React 的一些基本概念比较熟悉,例如组件、状态、属性和生命周期方法。此外,本教程不使用 Hooks,但我在即将推出的课程中会深入讲解 Hooks,并提供大量的实践练习。
1. 创建样板并渲染 App 组件
首先,我们需要创建应用程序的样板代码。为此,我们需要导入 `<module>`React和ReactDOM`<module>`,并使用它们ReactDOM来渲染一个名为 `<component>` 的组件App,该组件我们稍后会创建。然后,我们将该App组件放在根目录下。我们还需要App从其对应的 `<component>` 文件中导入 `<component>` "./App",该文件我们稍后也会创建。
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
然后我们创建App.js文件。在文件中,我们创建一个名为 `<function_component>` 的函数式组件App,目前它返回一个简单的 `<select>` <h1>。然后我们导出它。`<select>` 组件<h1>允许我们检查应用程序是否在屏幕上正确显示。
import React from 'react';
function App() {
return <h1>Hello world!</h1>;
}
export default App;
2. 创建 Header 和 MemeGenerator 组件
接下来,我们创建 Header 和 MemeGenerator 组件。Header 组件仅用于显示元素,而 MemeGenerator 组件会调用 API 并将数据保存在状态中。
我们先来创建Header.js文件。由于 Header 组件仅用于显示,因此它应该是一个函数式组件。目前,该组件应该返回一个简单的值<h1>。创建完成后,我们导出 Header 组件。
import React from 'react';
function Header() {
return <h1>HEADER</h1>;
}
export default Header;
接下来,我们创建MemeGenerator.js文件。由于该MemeGenerator组件将保存数据并调用 API,因此它必须是一个类组件。我们仍然需要导入 React,并且由于它将是一个类组件,我们Component还需要导入 `<React>`(这是一个命名导入)。
MemeGenerator 需要一个constructor()调用super(),并且由于它将保存状态,我们现在向其中添加一些空状态。与 Header 组件类似,我们<h1>首先渲染一个简单的元素。然后我们导出 MemeGenerator。
import React, { Component } from 'react';
class MemeGenerator extends Component {
constructor() {
super();
this.state = {}; //empty state
}
render() {
return <h1>MEME GENERATOR SECTION</h1>;
}
}
export default MemeGenerator;
现在,我们将 Header 和 MemeGenerator 导入到App.jsApp 组件中,并分别创建它们的实例。为了正确显示这些组件,我们将它们包裹在一个<div>.
import React from 'react';
import Header from './Header';
import MemeGenerator from './MemeGenerator';
function App() {
return (
<div>
<Header />
<MemeGenerator />
</div>
);
}
export default App;
3. 完成头部组件。
为了完善<Header>组件,我们添加一个带有“trollface”字样的图片,方法是插入一个<img>标签并将 src 属性设置为图片的 URL。然后,我们添加一个<p>包含应用名称的标签,并将这两个标签包裹在语义化的 HTML5<header>标签中。
function Header() {
return (
<header>
<img
src='http://www.pngall.com/wp-content/uploads/2016/05/Trollface.png'
alt='Problem?'
/>
<p>Meme Generator</p>
</header>
);
}
由于样式设置不在本课程的讨论范围之内,CSS 样式已经创建完毕并应用到<header>标签中。结果如下:
也就是说,学习者可以随时自行尝试不同的样式,磨练 CSS 技能。<Header/>现在这部分已经完成,挑战的其余部分将在……进行。<MemeGenerator/>
4. 初始化状态
现在我们需要初始化状态,以便保存顶部文本、底部文本和随机图像(已提供)。
为此,我们构建<MemeGenerator/>最初创建时放置在其中的空对象。我们将 ` topTexta` 和 ` b` 初始化bottomText为空字符串,并将 `c`初始化randomImg为提供的 URL。
class MemeGenerator extends Component {
constructor() {
super();
this.state = {
topText: '',
bottomText: '',
randomImg: 'http://i.imgflip.com/1bij.jpg'
};
}
}
5. 发起 API 调用
接下来,我们向提供的 URL 发起 API 调用,并将返回的数据(一个位于 `<state>` 中的数组response.data.memes)保存到名为 `<state>` 的新状态属性中allMemeImgs。
当我们需要从端点加载数据以供组件使用时,生命周期方法 `<state>` 是一个发出请求的好地方componentDidMount()。组件挂载后,我们立即使用原生fetch()函数 `<state>` 调用提供的 URL。
componentDidMount() {
fetch("https://api.imgflip.com/get_memes")
}
这会返回一个 Promise,我们使用该方法将其转换为 Javascript 对象.json()。
componentDidMount() {
fetch("https://api.imgflip.com/get_memes")
.then(response => response.json())
}
然后,我们通过从中提取 memes 数组来获得对我们有用的响应response.data。
componentDidMount() {
fetch("https://api.imgflip.com/get_memes")
.then(response => response.json())
.then(response => {
const { memes } = response.data
})
}
现在,我们将结果保存到一个名为 `save_results` 的新状态属性中allMemeImgs。为此,我们将其初始化allMemeImgs为一个空数组。
this.state = {
topText: '',
bottomText: '',
randomImg: 'http://i.imgflip.com/1bij.jpg',
allMemeImgs: []
};
现在,回到初始componentDidMount()状态,我们设置状态。由于我们不关心之前的状态是什么,所以我们将其设置allMemeImgs为 memes。
componentDidMount() {
fetch("https://api.imgflip.com/get_memes")
.then(response => response.json())
.then(response => {
const { memes } = response.data
this.setState({ allMemeImgs: memes })
})
}
为了确保其正常工作,我们console.log首先检查第一个项目,它看起来像这样:
以下是整个功能的概述componentDidMount()。
componentDidMount() { //ensure that data is fetched at the beginning
fetch("https://api.imgflip.com/get_memes") //call to URL
.then(response => response.json()) //turn promise into JS object
.then(response => {
const { memes } = response.data //pull memes array from response.data
console.log(memes[0]) // check data is present
this.setState({ allMemeImgs: memes }) // set allMemeImgs state
})
}
6. 创建输入表单
现在我们想创建一个表单,最终允许用户输入顶部和底部文本。我们使用一个 HTML<form>标签和一个<button>包含“Gen”字样的简单元素来实现这一点。我们使用预先提供的 CSS 设置其样式。
render() {
return (
<div>
<form className="meme-form">
<button>Gen</button>
</form>
</div>
)
}
7. 向表单添加输入字段
接下来,我们需要添加两个输入字段(一个用于顶部文本,一个用于底部文本)。该表单应为受控表单,因此我们需要添加所有必要的属性才能使其正常工作。我们onChange稍后会创建事件处理程序。
我们创建了两个输入字段,它们都具有类型text和相应的名称属性(topText和bottomText)。我们没有使用标签,而是使用占位符:“顶部文本”和“底部文本”。
最后,为了使之成为一个受控表单,我们将值设置为等于当前值,state并使用{this.state.topText}和{this.state.bottomText}。
render() {
return (
<div>
<form className="meme-form">
<input
type="text"
name="topText"
placeholder="Top Text"
value={this.state.topText}
/>
<input
type="text"
name="bottomText"
placeholder="Bottom Text"
value={this.state.bottomText}
/>
<button>Gen</button>
</form>
</div>
)
}
8. 创建 onChange 处理程序。
现在,我们创建 onChange 处理程序,它会在输入字段每次发生变化时更新相应的状态。
首先,我们创建一个handleChange()接收事件的函数。
handleChange(event) {
}
现在,我们将onChange两个输入字段的值设置为相等handleChange。
<form className='meme-form'>
<input
type='text'
name='topText'
placeholder='Top Text'
value={this.state.topText}
onChange={this.handleChange}
/>
<input
type='text'
name='bottomText'
placeholder='Bottom Text'
value={this.state.bottomText}
onChange={this.handleChange}
/>
<button>Gen</button>
</form>
我们需要记住在构造函数中绑定方法——这是 React 开发人员常犯的一个错误。
constructor() {
super()
this.state = {
topText: "",
bottomText: "",
randomImg: "http://i.imgflip.com/1bij.jpg",
allMemeImgs: []
}
this.handleChange = this.handleChange.bind(this)
}
为了测试新handleChange()功能,我们添加一个简单的console.log:
handleChange(event) {
console.log("Working!")
}
现在来完善这个handleChange()函数。为此,我们需要从 event.target 中提取 name 和 value 属性,以便获取要更新的状态名称(topText或bottomText)以及在文本框中输入的值。
handleChange(event) {
const { name, value } = event.target
}
现在我们将使用这些信息来更新状态。由于我们并不关心之前的状态是什么,所以我们只需提供一个对象,并将该对象的值设置[name]为输入字段中输入的值即可。
handleChange(event) {
const {name, value} = event.target
this.setState({ [name]: value })
}
9. 在顶部和底部文字旁边显示表情包图片
现在我们希望应用在顶部和底部文字旁边显示一张表情包图片。我们在 `<div>`<img>标签下方插入一个 `<div>` 标签<form>,并使用 `<div>` 标签将我们初始化的 `<div>` 设置randomImg为其源src={this.state.randomImg}。然后,我们添加两个 `<div>`<h2>标签来显示相应的文本,这些文本也保存在状态中。所有这些都被包裹在一个 `<div>` 标签中div,并使用预先提供的meme类进行样式设置。
<div className='meme'>
<img src={this.state.randomImg} alt='' />
<h2 className='top'>{this.state.topText}</h2>
<h2 className='bottom'>{this.state.bottomText}</h2>
</div>
现在我们可以通过在文本框中输入内容来测试该应用程序。由于每次按键都会正确设置状态,因此每次输入时,图像上显示的文本都会发生变化。
10. 在顶部和底部文字旁边显示随机的表情包图片
现在,我们需要创建一个方法,allMemeImgs当Gen按钮被点击时,该方法会从数组中随机选择一张图片并显示出来。数组中被选中图片的属性是.url……
我们可以把这个任务分解成更小的部分。
首先,我们将表单的值为onSubmit等于我们新方法的名称,我们将称之为handleSubmit()。
<form className="meme-form" onSubmit={this.handleSubmit}>
现在我们创建handleSubmit()上述函数render()。我们需要在事件上设置 preventDefault,否则该方法会尝试刷新页面。
handleSubmit(event) {
event.preventDefault()
}
我们还需要handleSubmit()在我们的系统中进行绑定constructor()。
constructor() {
super()
this.state = {
topText: "",
bottomText: "",
randomImg: "http://i.imgflip.com/1bij.jpg",
allMemeImgs: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
现在,我们需要获取一个随机数,从该索引中获取表情包,并将其设置randomImg为该.url随机项的值。
handleSubmit(event) {
event.preventDefault()
// get a random int (index in the array)
// get the meme from that index
// set `randomImg` to the `.url` of the random item I grabbed
}
为了获得一个随机数,我们使用…… Math.floor(Math.random)。为了确保它是allMemeImgs数组中的一个索引,我们将它乘以数组的长度。
const randNum = Math.floor(Math.random() * this.state.allMemeImgs.length);
现在我们将其设置randMemeImg为相等allMemeImgs,索引为allMemeImgs我们randNum刚刚得到的值。然后我们将.url其添加到末尾。
const randMemeImg = this.state.allMemeImgs[randNum].url;
现在,我们只需要通过更新 randomImg 属性来更新状态即可randMemeImg。
this.setState({ randomImg: randMemeImg });
我们最终完成的handleSubmit()函数如下所示:
handleSubmit(event) {
event.preventDefault()
const randNum = Math.floor(Math.random() * this.state.allMemeImgs.length)
const randMemeImg = this.state.allMemeImgs[randNum].url
this.setState({ randomImg: randMemeImg })
}
已完成的表情包生成器
我们现在已经完成了表情包生成器应用程序,每次点击按钮都会生成不同的图像Gen,然后将我们输入的文本叠加到图像上。
为了进一步学习,我们可以修改代码,看看能否改进它,或者尝试从不同的 API 获取图像。如果想进行一些更具挑战性的练习,我们甚至可以删除所有代码,然后从头开始重新编写。
恭喜你完成了教程,并掌握了本项目中使用的所有技能。
如果你准备好了,不妨看看我即将推出的高级课程,它将带你达到 React 专业水平!
文章来源:https://dev.to/bobziroll/learn-react-by-building-a-meme-generator-41gh













