乐趣区

一篇文章快速入门React框架

视频教程

本文章在 B 站配有视频教程

课程目标

  • 了解最常用的 React 概念和相关术语,例如 JSX,组件,属性(Props),状态(state)。
  • 构建一个非常简单的 React 应用程序,以阐述上述概念。

最终效果

创建 React 应用

helloworld

(1)安装 node.js     官网链接

(2)打开 cmd 窗口 输入   

npm install --g create-react-app 
npm install --g yarn

(-g 代表全局安装)

如果安装失败或较慢。需要换源,可以使用淘宝 NPM 镜像,设置方法为:npm config set registry https://registry.npm.taobao.org,设置完成后,

重新执行

cnpm install --g create-react-app
cnpm install --g yarn

安装 creat-react-app 功能组件,该组件可以用来初始化一个项目,即 按照一定的目录结构,生成一个新项目    

(3)在你想创建项目的目录下  例如 D:/project/ 打开 cmd 命令 输入   

create-react-app react-tutorial

去使用 creat-react-app 命令创建名字是 react-tutorial 的项目

安装完成后,移至新创建的目录并启动项目

cd react-tutorial
yarn start

一旦运行此命令,localhost:3000 新的 React 应用程序将弹出一个新窗口。

项目目录结构

一个 /public 和 /src 目录,以及 node_modules,.gitignore,README.md,和 package.json。

在目录 /public 中,重要文件是index.html,其中一行代码最重要

    <div id="root"></div>

该 div 做为我们整个应用的挂载点

/src目录将包含我们所有的 React 代码。

要查看环境如何自动编译和更新您的 React 代码,请找到文件 /src/App.js
将其中的

        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>

修改为

        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          和豆约翰 Learn React
        </a>

保存文件后,您会注意到 localhost:3000 编译并刷新了新数据。

现在删除 /src 目录中的所有文件,我们将从头开始创建自己的项目文件。

开始我们的 mini 项目

1. 新建文件 index.css
我们就拷贝 index.css 文件的全部内容(css 不是我们这次课程的重点).
2. 新建文件 index.js
在 index.js 中,我们将导入 React,ReactDOM 和 CSS 文件。
src / index.js

import React, {Component}  from 'react'
import ReactDOM from 'react-dom'
import './index.css'

创建 App 组件,在 render 方法中返回一个 div(这里是 JSX 语法)

src / index.js

class App extends Component {render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

最后,我们将 App 渲染到根节点。
src / index.js

ReactDOM.render(<App />, document.getElementById('root'))

index.js 完整代码

import React, {Component}  from 'react'
import ReactDOM from 'react-dom'
import './index.css'


class App extends Component {render() {
        return (
            <div className="App">
                <h1>Hello, React!</h1>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('root'))

在浏览器输入 localhost:3000,您将看到“你好,React!

JSX:JavaScript + XML

如您所见,我们在 React 代码中一直使用看起来像 HTML 的东西,但它并不是完全 HTML。这是 JSX,代表 JavaScript XML。

使用 JSX,我们可以编写看起来像 HTML 的内容,也可以创建和使用我们自己的类似 XML 的标签。
以下是 JSX 分配给变量的样子:

const heading = <h1 className="site-heading">Hello, React</h1>

编写 React 并非必须使用 JSX。在幕后,它正在运行 createElement,它接受标签,包含属性象和组件的后代,并呈现相同的信息。下面的代码将具有与上面的 JSX 相同的输出。

const heading = React.createElement('h1', { className: 'site-heading'}, 'Hello, React!')

JSX 实际上更接近 JavaScript,而不是 HTML,因此在编写时需要注意一些关键区别。

  • className 用于代替 class 添加 CSS 类(classJavaScript 中的保留关键字)。
  • JSX 中的属性和方法为 camelCase(驼峰表示法)– onclick 将变为 onClick。
  • 自闭合标签必须以斜杠结尾 - 例如 <img />
  • JavaScript 表达式也可以使用大括号(包括变量,函数和属性)嵌入 JSX 内。
const name = 'Tania'
const heading = <h1>Hello, {name}</h1>

JSX 比在原生 JavaScript 中创建和添加元素更容易编写和理解,这也是人们如此热爱 React 的原因之一。

组件

到目前为止,我们已经创建了一个组件 - App 组件。React 中的几乎所有内容都由组件组成,这些组件可以是类组件或简单组件。
大多数 React 应用程序都有许多小组件,所有内容都加载到主 App 组件中。
组件经常定义在单个 js 文件中,
接下来让我们更改项目。从 index.js 中删除 App 类:
index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'

ReactDOM.render(<App />, document.getElementById('root'))

我们将创建一个新文件 App.js,并将组件放入其中。

import React, {Component} from 'react'

class App extends Component {render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

export default App

我们将组件导出为 App 并将其加载到中 index.js。将组件分成文件不是强制性的,但是如果不这样做的话,应用程序将开始变得笨拙和混乱。

类组件

让我们创建另一个组件。我们将创建一个表格。制作 Table.js,并用以下数据填充。
src / Table.js

import React, {Component} from 'react'

class Table extends Component {render() {
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Job</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Charlie</td>
            <td>Janitor</td>
          </tr>
          <tr>
            <td>Mac</td>
            <td>Bouncer</td>
          </tr>
          <tr>
            <td>Dee</td>
            <td>Aspiring actress</td>
          </tr>
          <tr>
            <td>Dennis</td>
            <td>Bartender</td>
          </tr>
        </tbody>
      </table>
    )
  }
}

export default Table

我们创建的该组件是一个自定义类组件。自定义组件名称首字母大写,以区别于常规 HTML 元素。回到 App.js,我们可以先将表格组件导入,以加载表格组件:
src / App.js

import Table from './Table'

然后将其加载到 App 类的 render()函数中,在此之前我们已经有了“Hello,React!”。还更改了外部容器的类名。

import React, {Component} from 'react'
import Table from './Table'

class App extends Component {render() {
    return (
      <div className="container">
        <Table />
      </div>
    )
  }
}

export default App

现在,我们已经了解了什么是自定义类组件。我们复用此组件。但是,由于数据已被硬编码到其中,因此目前它并不太有用。

简单组件

React 中的另一种类型的组件是 simple component,它是一个函数。该组件不使用 class 关键字。让我们为 Table 组件制作两个简单的子组件 - 一个表头和一个表主体。

我们将使用 ES6 箭头表达式来创建这些简单的组件。首先,表头:
src / Table.js

const TableHeader = () => {
  return (
    <thead>
      <tr>
        <th>Name</th>
        <th>Job</th>
      </tr>
    </thead>
  )
}

然后是表主体。
src / Table.js

const TableBody = () => {
  return (
    <tbody>
      <tr>
        <td>Charlie</td>
        <td>Janitor</td>
      </tr>
      <tr>
        <td>Mac</td>
        <td>Bouncer</td>
      </tr>
      <tr>
        <td>Dee</td>
        <td>Aspiring actress</td>
      </tr>
      <tr>
        <td>Dennis</td>
        <td>Bartender</td>
      </tr>
    </tbody>
  )
}

现在,我们的 Table.js 文件将如下所示。请注意,TableHeader 和 TableBody 组件都在同一个文件中,并由 Table 类组件使用。
src / 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>
            <td>Charlie</td>
            <td>Janitor</td>
        </tr>
        <tr>
            <td>Mac</td>
            <td>Bouncer</td>
        </tr>
        <tr>
            <td>Dee</td>
            <td>Aspiring actress</td>
        </tr>
        <tr>
            <td>Dennis</td>
            <td>Bartender</td>
        </tr>
        </tbody>
    )
}


class Table extends Component {render() {
        return (
            <table>
                <TableHeader />
                <TableBody />
            </table>
        )
    }
}

export default Table

运行结果不变。如您所见,组件可以嵌套在其他组件中,并且简单和类组件可以混合使用。

一个类组件必须包含 render(),并且 return 只能返回一个父元素。

作为总结,让我们比较一个简单的组件和一个类组件:.

简单组件

const SimpleComponent = () => {return <div>Example</div>}

类组件

class ClassComponent extends Component {render() {return <div>Example</div>}
}

props

我们的 Table 组件,数据是硬编码的。关于 React 的重要问题之一是它如何处理数据,它使用属性(props)和状态(state)来处理数据。现在,我们将专注于使用 props 处理数据。

首先,让我们从 TableBody 组件中删除所有数据。
src / Table.js

const TableBody = () => {
    return (<tbody></tbody>)
}

然后,将所有数据定义到一个对象数组中:
src / App.js

class App extends Component {render() {
    const characters = [
      {
        name: 'Charlie',
        job: 'Janitor',
      },
      {
        name: 'Mac',
        job: 'Bouncer',
      },
      {
        name: 'Dee',
        job: 'Aspring actress',
      },
      {
        name: 'Dennis',
        job: 'Bartender',
      },
    ]

    return (
      <div className="container">
        <Table />
      </div>
    )
  }
}

接下来,我们将通过属性 characterData 将数据传递给子组件 Table,传递的数据是 characters 变量,由于它是 JavaScript 表达式,因此使用大括号括起来。

return (
  <div className="container">
    <Table characterData={characters} />
  </div>
)

现在数据已经传递到 Table 组件,我们可以在 Table 组件通过 this.props 中访问到。
src / Table.js

class Table extends Component {render() {const { characterData} = this.props

    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} />
      </table>
    )
  }
}

由于 Table 组件实际上由两个较小的简单组件组成,因此再次通过 props 将其传递给子组件 TableBody

我们将把 props 作为参数传递给简单组件 TableBody,并通过数组的 map 方法将数据映射为 jsx 片段的集合。该 jsx 片段集合将包含在 rows 变量中,我们将其作为表达式返回。

const TableBody = props => {const rows = props.characterData.map((row, index) => {
    return (<tr key={index}>
        <td>{row.name}</td>
        <td>{row.job}</td>
      </tr>
    )
  })

  return <tbody>{rows}</tbody>
}

您会注意到我已经向每个表行添加了一个键索引。在 React 中创建列表时,应始终使用键,因为它们有助于识别每个列表项。我们还将在需要操纵列表项的时刻看到这是必要的。

props 是将现有数据传递到 React 组件的有效方法,但是该组件无法更改 props- 它们是只读的。在下一节中,我们将学习如何使用状态(state)来进一步控制 React 中的数据处理。

state

我们将表格数据存储在数组变量中,并将其作为 props 传递。但是如果我们希望能够从数组中删除一个项目,就做不到了。props,是一种单向数据流,子组件不能进行修改。但是有了 state,我们就可以更新组件中的私有数据。

您可以将 state 视为可以在组件内增删改的而不必添加到数据库的任何临时数据 - 例如,在确认购买之前在购物车中添加和删除的购物车项目。state 改变后,绑定 state 数据的视图会自动更新。

首先,我们将创建一个 state 对象。
src / App.js

class App extends Component {state = {}
}

在 state 对象中定义属性,保存我们所需的数据。
src / App.js

class App extends Component {
  state = {characters: [],
  }
}

将我们之前创建的对象的整个数组移到中 state.characters。

class App extends Component {
  state = {
   characters : [
            {
                name: 'Charlie',
                job: 'Janitor',
            },
            {
                name: 'Mac',
                job: 'Bouncer',
            },
            {
                name: 'Dee',
                job: 'Aspring actress',
            },
            {
                name: 'Dennis',
                job: 'Bartender',
            },
        ]
  }
}

在 App.js 中,创建 removeCharacter 方法来删除一个项目

removeCharacter = index => {const { characters} = this.state

  this.setState({characters: characters.filter((character, i) => {return i !== index}),
  })
}

您必须使用 this.setState()来修改数组。简单地将新值应用到 this.state.property 将不起作用。

现在,我们必须将该函数传递给组件。我们会将 removeCharacter 函数作为属性传递给 Table。
src/App.js

render() {const { characters} = this.state

  return (
    <div className="container">
      <Table characterData={characters} removeCharacter={this.removeCharacter} />
    </div>
  )
}

由于 Table 组件,并不需要自己的 state 数据对象,所以,我们将它改造成简单组件,并将 removeCharacter 函数继续向 TableBody 子组件传递:
src/Table.js

const Table = props => {const { characterData, removeCharacter} = props

  return (
    <table>
      <TableHeader />
      <TableBody characterData={characterData} removeCharacter={removeCharacter} />
    </table>
  )
}

在 TableBody 组件中,我们将键 / 索引作为参数传递,因为 removeCharacter 函数知道要删除的项目索引。我们创建一个按钮并将其 onClick 事件绑定 removeCharacter 函数;
src/Table.js

<tr key={index}>
  <td>{row.name}</td>
  <td>{row.job}</td>
  <td>
    <button onClick={() => props.removeCharacter(index)}>Delete</button>
  </td>
</tr>

onClick 事件必须绑定为返回该 removeCharacter()方法的函数,否则 removeCharacter()将尝试不等按钮点击而自动运行。

新增表格数据

现在我们已经将数据存储在 App.js 组件的 state 对象中,并且可以从 state 对象中删除任何项目。但是,如何向 state 对象中添加数据呢?我们将通过一个表单组件来实现这个需求。

首先,state.characters 中删除所有硬编码数据:
src / App.js

class App extends Component {
  state = {characters: [],
  }
}

创建新文件 Form.js。

import React, {Component} from 'react'

class Form extends Component {
  initialState = {
    name: '',
    job: '',
  }

  state = this.initialState
}

以前,React 类组件有必要包括一个 constructor(),但是现在不再需要。

此表单的目标是 Form 每次更改表单中的字段时都会更新本组件(Form)的 state,并且在我们提交表单时,所有数据都将传递给 App 组件的 state,然后 Table 组件会自动更新。

首先定义 input 控件的 onChange 事件处理函数:

src / Form.js

handleChange = event => {const { name, value} = event.target

  this.setState({[name]: value,
  })
}

Form 组件的 render 函数实现如下:
src / Form.js

render() {const { name, job} = this.state;

  return (
    <form>
      <label htmlFor="name">Name</label>
      <input
        type="text"
        name="name"
        id="name"
        value={name}
        onChange={this.handleChange} />
      <label htmlFor="job">Job</label>
      <input
        type="text"
        name="job"
        id="job"
        value={job}
        onChange={this.handleChange} />
    </form>
  );
}

export default Form;

在 App.js,导入 Form:

import Form from './Form';

return (
  <div className="container">
    <Table characterData={characters} removeCharacter={this.removeCharacter} />
    <Form />
  </div>
)

最后一步是实现提交该数据并更新父组件状态。我们将创建一个名为 handleSubmit() 的函数,该函数将使用 [ES6 扩展运算符] 语法来更新状态。
src / App.js

handleSubmit = character => {this.setState({ characters: [...this.state.characters, character] })
}

确保我们将其作为参数传递给Form

<Form handleSubmit={this.handleSubmit} />

现在,在 Form 中,我们将创建一个名为 submitForm() 的方法,该方法将调用传进来的 handleSubmit 函数,并将 Form 的 state 作为 character 参数传递。它还将 Form 的 state 重置为初始状态,以在表单提交后清除表单信息。

src / Form.js

submitForm = () => {this.props.handleSubmit(this.state)
  this.setState(this.initialState)
}

最后,我们将在 Form.js 中添加一个提交按钮以提交表单,点击将调用我们刚定义的 submitForm 函数。
src / Form.js

<input type="button" value="Submit" onClick={this.submitForm} />

就是这样!该应用程序已完成。

完整代码

退出移动版