React 指引
最近在学 React, 看到一篇博客写的不错,可惜是英文版,正巧无事,翻译过来吧。
原文地址: https://www.taniarascia.com/getting-started-with-react/
准备
在开始使用 React 之前,有一些事情需要准备好:
- 对 HTML & CSS 有一个基本的了解;
- 了解 JavaScript;
- 对 DOM 有基本的认识;
- 熟悉 ES6 语法和特征;
- 安装好 Node.js 和 npm.
注:HTML, CSS, JS, DOM 可以通过 W3School 学习;Node.js 和 npm 的安装教程网上一大堆。
目标
- 掌握基本的 React 概念和有关的组件,例如 Babel, Webpack, JSX, components, props, state, lifecycle.
- 通过以上概念建立一个简单的React app.
什么是 React
- React 是 JavaScript 中最流行的库;
- 不同于 Angular, React 并不是一个框架;
- React 是 Facebook 创建的一个开源项目;
- React 用来构建 UI 界面;
- React 是 MVC 的视图层;
安装
两种安装方式:
- 静态 HTML 文件
首先要明白,这种方式并不常用,但可以帮助我们快速使用 React.
首先,我们建立一个index.html
文件,通过head
标签下的三个script
,可将 React, React DOM, Babel 加载到其中。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Hello React!</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// React code will go here
</script>
</body>
</html>
在<div>
标签下的<script>
,可以输入代码,不妨写一个 Hello World:
class App extends React.Component{//使用ES6类来创建React组件
render() {
return <h1>Hello World</h1>;//JSX语法
}
}
ReactDOM.render(//使用该方法来调用 App 类
<App/>,
document.getElementById('root')
)
用浏览器打开上述 html 文件,结果为:
- 创建 React App
方法 1 并不高效,这里介绍更常用的方法。
首先确保电脑安装了 Node.js
打开终端(Windows下的cmd),输入指令 npx create-react-app react-tutorial
, 稍等,安装好后,会出现一个 react-tutorial
文件夹,用指令cd react-tutorial
打开该文件夹,在该文件下,输入指令npm start
便启动了该项目,随后,会弹出一个地址为localhost:3000
的浏览器窗口。
样式如下:
下面,来看文件夹 react-tutorial
的结构。
/public
,最重要的文件是index.html
.
/src
文件夹中存放了 React 代码。
现在,为了编写我们自己的项目,需要删除/src
文件夹下的所有文件,然后在该目录下创建我们自己的代码文件:index.css
和index.js
.
css格式是负责样式,因此对于index.css
文件,可以自己写一个布局,也可以直接copy别人的代码,并无大碍。这里选择原作者给的样式,地址为:https://taniarascia.github.io/primitive/css/main.css 将该文本的内容直接拷贝到index.css
中即可。
对于index.js
,首先需要导入一些头文件:
import React,{Component} from "react";
import ReactDOM, {render} from "react-dom"
import './index.css' //调用index.css
下面开始编辑内容,从Hello React开始:
注意,在下属代码中,我们用className
取代HTML中的class
.这是因为以下代码皆为 JavaScript 代码,而不是HTML代码。
class App extends React.Component{
render() {
return(
<div className="App">
<h1>Hello React</h1>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
此时,在浏览器中输入地址http://localhost:3000/
将会看到:
插件:React Developer Tools
为了更好地在浏览器中调试 React 代码,建议使用浏览器插件 React Developer Tool. 例如Chrome中,直接打开Chrome商店下载即可。
下载完成后,当使用 F12 进入调试界面即可。
JSX: JavaScript + XML
在上述index.js
中,我们使用了一种类似于 HTML 的代码:
<div className="App">
<h1>Hello React</h1>
</div>
但它并不是HTML, 而是JSX: JavaScript XML.
首先,需要讲清楚的是:JSX 在 React 中是可用可不用的,但是为了方便,我们采用 JSX 格式。
这里给出使用 JSX 时需要注意的几点:
className
用来代替class
,因为class
在JS中是一个关键字。- JSX 采用骆驼明明法,例如:
onclick
在JSX中应该变为onClick
. - 单括号必须用’/'结束,例如
<img/>
. - 在JSX中,可以使用’{}'来代表变量。
Components
在index.js
中,我们创建了App
组件。
但是对于许多 React 而言,通常存在多个小组件,为了方便,每个组件通常都有自己对应的文件,我们的项目也需要改一下:
在同目录下(即/src
目录下)新建一个App.js
, 将index.js
中的 class App extends React.Component
移动到App,js
中,最后,两个文件的代码如下:
App.js:
import React,{Component} from "react";
class App extends React.Component{
render() {
return(
<div className="App">
<h1>Hello React</h1>
</div>
);
}
}
export default App
index.js:
import React,{Component} from "react";
import ReactDOM, {render} from 'react-dom'
import './index.css' //调用index.css
import App from './App.js'
ReactDOM.render(
<App />,
document.getElementById('root')
);
Class Components
下面,我们创建一个 Table 组件。
首先,在 src 目录下创建 Table.js
, 其代码如下所示:
import React,{Component} from "react";
class Table extends React.Component{
render() {
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Job</th>
</tr>
</thead>
<tbody>
<tr>
<th>张三</th>
<th>码农</th>
</tr>
<tr>
<th>李四</th>
<th>程序员</th>
</tr>
</tbody>
</table>
);
}
}
export default Table
返回到 App.js
中,为了加载 Table.js
, 在导入头文件的代码上添加一行import Table from './Table'
.
添加上述代码后,在 App.js
中可以使用 <Table/>
.
在之前,我们输出了 “Hello React”, 现在删除App类中的<h1>Hello React</h1>
,将其内容更改为<Table/>
.
此时,App.js
全部内容为:
import React,{Component} from "react";
import Table from './Table'
class App extends React.Component{
render() {
return(
<div className="App">
<Table/>
</div>
);
}
}
export default App
再登入http://localhost:3000/
,展示的内容为:
到目前为止,我们创建了一个表格 Table。
Simple Components
Components 包含两类:1. Class Components; 2. Simple Components.
上一节我们介绍了Class Components, 这一节我们介绍 Simple Components.
Simple Components 是一个函数,不使用关键字class
.
例如,对于上一小节的Table.js
,使用 Class Components 时有一个class Table.
我们使用 Simple Components 可以将其更改为两个函数: table header 和 table body.
更改后的Table.js
:
import React,{Component} from "react";
const TableHeader = () =>{
return (
<thead>
<tr>
<th>Name</th>
<th>Job</th>
</tr>
</thead>
)
};
const TableBody = () =>{
return(
<tbody>
<tr>
<th>张三</th>
<th>码农</th>
</tr>
<tr>
<th>李四</th>
<th>程序员</th>
</tr>
</tbody>
)
};
class Table extends Component {
render() {
return (
<table>
<TableHeader />
<TableBody />
</table>
)
}
}
export default Table
此时,在网页上现实的结果并不发生变化。正如你所看到的,simple/class components 可以混合使用。
Props
现在,我们有了 Table 组件,但数据是不可修改的。React 的使用 props 和 states 操控数据,现在我们首先来看 props.
首先,将组件 TableBody 中的数据全部剪贴:
const TableBody = () =>{
return <tbody/>
};
然后数据以列表的形式移动到App.js
:
class App extends React.Component{
render() {
const characters = [
{
name: '张三',
job: '码农',
},
{
name: '李四',
job: '程序员',
},
];
return(
<div className="container">
<Table/>
</div>
);
}
}
现在,我们要将数据传递给 Table, 我们将传递的属性名定义为 characterData
, 它的作用是传递变量characters
.
App.js
中的return
语句更改为如下:
return(
<div className="container">
<Table characterData={characters}/>
</div>
);
现在,数据已经传递到Table
中了,我们需要在Table.js
中访问这些数据。
在Table.js
中,我们将其中 Table 类的代码更改为如下:
class Table extends Component {
render() {
const {characterData} = this.props;
return (
<table>
<TableHeader />
<TableBody characterData={characterData}/>
</table>
);
}
}
现在,登入到浏览器中,使用 React 开发者工具,可以看到:
这表明数据已经以虚拟DOM的形式存储下来,但是并不是真正的DOM. 因此,此时在浏览器上无法显示表格。
为了显示表格,我们应该在 TableBody 中接受数据,在这里,我们使用map来返回一个表格航。
更改后的 Table.js
的 TableBody 如下:
const TableBody = props =>{
const rows = props.characterData.map((rows, index)=>{
return (
<tr key={index}>
<td>{rows.name}</td>
<td>{rows.job}</td>
</tr>
)
})
return <tbody>{rows}</tbody>
}
此时再看前端网页,数据已经显示上了:
props 是一个高效的传递数据的方法,但可惜,这样传递数据的方式仅仅是只读的。
State
在上一节中,我们将数据以序列变量的形式通过 props 传递。但是,如果我们想删除从表格中删除一行,props 是无法做到的。
因此我们需要 state.
首先,我们在App.js
中创建一个 state
对象:
class App extends React.Component{
state = {
characters: [],
};
}
接着,将数据移动到 characters 中,同时删除 render() 里的 character.
class App extends React.Component{
state = {
characters: [
{
name: '张三',
job: '码农',
},
{
name: '李四',
job: '程序员',
},
],
};
}
为了实现删除一行的目的,我们在 App 类中添加一个removeCharacter
方法:
removeCharacter = index => {
const { characters } = this.state
this.setState({
characters: characters.filter((character, i) => {
return i !== index
}),
})
}
此处,filter的实现和功能不再赘述。
之后,我们需要将函数传递到组件中,具体做法是在render中添加一个 button 来调用者函数。
为实现上述需求,首先将App.js
中的removeCharacter
函数以 props 的方式传递到Table
中,先将App.js
的 render() 更改为如下:
render() {
const { characters } = this.state
return(
<div className="container">
<Table characterData={characters} removeCharacter={this.removeCharacter}/>
</div>
);
}
此时,对于Table.js
文件,在Table中接受该函数:
class Table extends Component {
render() {
const {characterData, removeCharacter} = this.props;
return (
<table>
<TableHeader />
<TableBody characterData={characterData} removeCharacter={removeCharacter}/>
</table>
);
}
}
之后,需要从 Table 中传递到 TableBody 中,同时添加一个删除的按钮:
const TableBody = props =>{
const rows = props.characterData.map((rows, index)=>{
return (
<tr key={index}>
<td>{rows.name}</td>
<td>{rows.job}</td>
<td>
<button onClick={ () => props.removeCharacter(index)}>Delete</button>
</td>
</tr>
)
})
return <tbody>{rows}</tbody>
};
登入浏览器后,显示结果如下:
点击第一个 delete 后的结果为:
提交表格数据
我们将数据存储到 state 中,并且实现了删除表项,然而,我们如何添加新的数据到 state 中?
首先,为了方便,我们将App.js
中App类下的state中的所有表项删除:
state = {
characters: [],
};
接下来我们创建一个Form.js
用来存储Form
组件,在该组件中,首先使用一个构造函数constructor
, 用来接受父亲的props.
另外,给Form
中的对象一个初始化,并将该初始化赋值给state.
此时,Form.js
的代码如下:
import React, { Component } from 'react'
class Form extends Component {
constructor(props) {
super(props)
this.initialState = {
name: '',
job: '',
}
this.state = this.initialState
}
}
export default Form;
我们的目标是,当Form
中的state每次发生变化时提交表格,将数据传递给App
中的 state, 接着再从App
中传递到Table
,更新表格。
首先,我们在Form.js
中创建一个函数 handleChange
,每次有提交时便运行该函数。
handleChange = event =>{
const {name, value} = event.target
this.setState({
[name]:value,
})
}
之后,在Form.js
创建render, 将上述构建的 name, value 转化为 name, job:
render() {
const { name, job } = this.state;
return (
<form>
<label for="name">Name</label>
<input
type="text"
name="name"
id="name"
value={name}
onChange={this.handleChange} />
<label for="job">Job</label>
<input
type="text"
name="job"
id="job"
value={job}
onChange={this.handleChange} />
</form>
);
}
随后,在App.js
中,render更改为如下:
render() {
const { characters } = this.state
return(
<div className="container">
<Table characterData={characters} removeCharacter={this.removeCharacter}/>
<Form/>
</div>
);
}
此时来看前端页面:
此时,随便输入一个name 和 job, 在 React 开发者工具中,我们已经可以看到,并没有submit。仅仅是在Form中更新了。
之后,让我们开始书写submit代码。
在```App.js``中,添加一个 handleSubmit函数:
handleSubmit = character => {
this.setState({ characters: [...this.state.characters, character] })
}
之后将render中return的<Form>
更改为<Form handleSubmit={this.handleSubmit} />
.
现在,返回到Form.js
,创建一个函数submitForm()
,然后将Form
的state传递出去。
submitForm = () => {
this.props.handleSubmit(this.state)
this.setState(this.initialState)
}
最后,在render中添加一个“提交按钮”:
render() {
const { name, job } = this.state;
return (
<form>
<label for="name">Name</label>
<input
type="text"
name="name"
id="name"
value={name}
onChange={this.handleChange} />
<label for="job">Job</label>
<input
type="text"
name="job"
id="job"
value={job}
onChange={this.handleChange} />
<input type="button" value="Submit" onClick={this.submitForm} />
</form>
);
}
最终显示的效果如下:
随便写两个表格提交后的效果: