React 18 组件化入门:从 Hello React 到类组件实战

作者:小积木时间:2026-10-26 06:16:54浏览:428

本文通过 Hello React 案例演示 React 18 的组件化开发模式。详细讲解 JSX 语法、React 18 前后渲染 API 的变化(createRoot vs render),以及类组件的封装方法。重点剖析 state 状态管理、this 指向问题及解决方案,并提供电影列表与计数器等实战练习。

React 初体验

接下来,我们将通过 Hello React 这一基础案例,直观感受 React 的开发模式,并初步接触 JSX 语法的使用。

Hello React 案例演练

第一步:引入 React 开发所需的依赖库。

<!-- crossorigin用来解决跨域 -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

第二步:在编写 React 的 script 标签时,必须添加 type="text/babel" 属性。这一步至关重要,它的作用是告知浏览器使用 Babel 来解析 JSX 语法,从而正确执行代码。

<script type="text/babel">
  // jsx语法编写react代码
</script>

第三步:创建元素并将其渲染到页面上。

在 React 18 版本之前,通常使用 ReactDOM.render() 函数来进行渲染操作。该函数需要接收两个参数。

第一个参数:指定需要渲染的 JSX 内容。

第二个参数:指定将内容挂载到的具体 HTML 元素节点。

<!-- 挂载的HTML元素 -->
<div id="app"></div>

<!-- crossorigin用来解决跨域 -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<!-- jsx语法编写react代码 -->
<script type="text/babel">
  // React18之前的做法
  
  ReactDOM.render(<h2>Hello World</h2>, document.querySelector("#app"))
</script>

在 React 18 及之后的版本中,推荐使用 ReactDOM. createRoot() 函数进行渲染。该函数只接收一个参数,用于创建一个 React 根(Root)。渲染的内容将被包含在这个根实例中(虽然可以创建多个根,但通常项目只使用一个根)。

<!-- 挂载的元素 -->
<div id="app"></div>
<div id="root"></div>

<!-- crossorigin用来解决跨域 -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<!-- jsx语法编写react代码 -->
<script type="text/babel">
  // React18之后的做法
  
  const app = ReactDOM.createRoot(document.querySelector("#app"))
  const root = ReactDOM.createRoot(document.querySelector("#root"))
</script>

随后,通过调用 root.render() 函数向该根组件中渲染具体的元素,参数即为需要渲染的 JSX 元素。

<!-- 挂载的元素 -->
<div id="app"></div>
<div id="root"></div>

<!-- crossorigin用来解决跨域 -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<!-- jsx语法编写react代码 -->
<script type="text/babel">
  // React18之后的做法
  const app = ReactDOM.createRoot(document.querySelector("#app"))
  app.render(<h2>Hello React</h2>)

  const root = ReactDOM.createRoot(document.querySelector("#root"))
  root.render(<h2>Hello React18</h2>)
</script>

React 初体验

Hello React 案例升级

为了更好地演练 React 的功能,我们提出一个小型的需求场景:

在界面上显示一段文本:“Hello World”。

在界面下方放置一个按钮,当用户点击该按钮后,将显示的文本内容更改为“Hello React”。

React 初体验

第一步:将初始元素渲染到页面中。

在 JSX 语法中,可以通过 {} 的方式将 JavaScript 变量嵌入到标签中。大家先对这种语法形式有个初步印象即可,后续章节会详细讲解 JSX 语法的规范。

<!-- 挂载的元素 -->
<div id="app"></div>

<!-- crossorigin用来解决跨域 -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

<!-- jsx语法编写react代码 -->
<script type="text/babel">
  const app = ReactDOM.createRoot(document.querySelector("#app"))

  // 将文本定义成变量
  let message = "Hello World"

  // 第二个小括号表示括号内容是一个整体
  app.render((
    <div>
      <h2>{message}</h2>
      <button>改变文本</button>
    </div>
  ))
</script>

第二步:监听按钮的点击事件,并据此修改 message 变量的值。

在 JSX 中监听按钮点击事件,是通过 onClick 属性绑定一个回调函数来实现的。请注意,属性名中的首字母 C 必须是大写的。

const app = ReactDOM.createRoot(document.querySelector("#app"))

// 将文本定义成变量
let message = "Hello World"

// 定义一个函数, 处理按钮点击事件
function btnClick() {
  // 修改数据
  message = "Hello React"

  // 修改完成后需要重新渲染界面
  app.render((
    <div>
      <h2>{message}</h2>
      <button onClick={btnClick}>改变文本</button>
    </div>
  ))
}

// 第二个小括号表示括号内容是一个整体
app.render((
  <div>
    <h2>{message}</h2>
    <button onClick={btnClick}>改变文本</button>
  </div>
))

第三步:简化代码结构。观察上述代码可以发现存在重复逻辑,我们可以将这些重复代码抽取到一个独立的函数中进行封装,以提高代码的可读性和复用性。

const app = ReactDOM.createRoot(document.querySelector("#app"))

// 将文本定义成变量
let message = "Hello World"

// 定义一个函数, 处理按钮点击事件
function btnClick() {
  // 修改数据
  message = "Hello React"

  // 修改完成后需要重新渲染界面
  rootRander()
}

rootRander()
// 封装一个渲染函数
function rootRander() {
  // 第二个小括号表示括号内容是一个整体
  app.render((
    <div>
      <h2>{message}</h2>
      <button onClick={btnClick}>改变文本</button>
    </div>
  ))
}

Hello React 的组件化

组件化的实现方式

回顾 Hello React 的整个逻辑,它其实可以被视为一个整体。因此,我们可以将整个业务逻辑封装成一个独立的组件。

root.render() 函数的第一个参数不仅可以接收 HTML 元素,也可以接收一个自定义组件。

因此,我们可以先将之前的业务逻辑封装到一个 组件 中,然后将该组件作为参数传入到 root.render 函数的第一个位置。

在 React 中,具体如何封装一个组件呢?

React 中的组件主要分为两种类型:类组件和函数式组件。

在本案例中,我们暂时采用类(Class)的方式来封装组件。

首先定义一个类,类名必须大写(这是组件命名的强制规范,若使用小写会被 React 误认为是原生 HTML 元素)。该类需要继承自 React.Component 。

// 类继承自React.Component, 才是组件
class App extends React.Component{}

接着,实现当前组件的 render 函数。render 函数中返回的 JSX 内容,就是 React 后续会自动帮我们渲染到页面上的视图。

class App extends React.Component {
  // 类中render函数的返回值, 就是要渲染到页面的内容
  render() {
    return (
      <div>
        <h2>Hello World</h2>
        <button>改变文本</button>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

数据依赖与状态管理

组件化开发中的问题一:数据应该在哪里定义?

在组件内部,我们可以将数据划分为两类:

参与界面更新的数据:当这类数据发生变化时,需要触发组件的重新渲染以反映最新状态。

不参与界面更新的数据:当这类数据发生变化时,不需要触发组件的重新渲染。

那些参与界面更新的数据,我们通常称之为参与数据流。这类数据定义在当前类实例的 state 属性中(注意,state 这个属性名是固定的,不可随意更改)。

我们可以在类的构造函数(constructor)中,通过 this.state = {定义的数据} 的方式初始化状态数据。

当我们的数据发生变化时,可以调用 this.setState 方法来更新数据,并通知 React 执行 update(更新)操作。

在执行 update 操作时,React 会重新调用 render 函数,并使用最新的数据来重新渲染界面,从而保证视图与数据同步。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      message: "Hello World"
    }
  }

  // 类中render函数的返回值, 就是要渲染到页面的内容
  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button>改变文本</button>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

事件绑定与 this 指向

组件化开发中的问题二:事件绑定中的 this 指向问题。

如果在类中直接定义一个函数,并将该函数绑定到元素的 onClick 事件上,那么问题来了:在这个函数内部,this 指向的是谁?

默认情况下,在类方法中直接绑定的事件处理函数,this 指向的是 undefined。

这看起来有些奇怪,为什么是 undefined 呢?

因为在传统的原生 DOM 操作中,监听点击事件时,监听函数内部的 this 通常指向触发事件的节点对象(例如 button 元素本身)。

然而,React 并不是直接渲染真实的 DOM 节点,我们所编写的 button 标签只是一种语法糖,其本质是 React 的 Element 对象。

因此,在 React 执行监听函数时,并没有像原生 DOM 那样自动绑定 this,导致默认情况下 this 为 undefined。

在绑定的函数中,我们往往需要使用当前组件实例,例如调用 this.setState 函数。为了做到这一点,我们必须确保能拿到当前组件对象的 this 引用。

因此,在传入函数时,我们需要手动为该函数绑定正确的 this 上下文。

可以采用如下写法:<button onClick={this.changeText.bind(this)}>改变文本</button>,即在事件绑定时使用 bind 方法显式绑定 this。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      message: "Hello World"
    }
  }

  btnClick() {
    // setState内部会做两件事情: 1.将state中的值改掉 2.自动执行render函数渲染页面
    this.setState({
      message: "Hello React"
    })
  }

  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={this.btnClick.bind(this)}>改变文本</button>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

如果有多个按钮,每次都需要编写 this.btnClick.bind(this) 会显得比较繁琐。因此,React 中还有另一种常见的写法:在 constructor 构造函数中提前绑定 this。这样在模板中就可以直接使用方法名,无需每次调用 bind。

这也是 React 官方文档中推荐的做法之一。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      message: "Hello World"
    }
    // 为方法提前绑定this
    this.btnClick = this.btnClick.bind(this)
  }

  btnClick() {
    this.setState({
      message: "Hello React"
    })
  }

  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={this.btnClick}>改变文本</button>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

其他案例练习

电影列表展示

首先定义一个数组来存放电影数据,然后对电影列表进行展示。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      movies: ["大话西游", "黑话律师", "独行月球"]
    }
  }

  render() {
    return (
      <div>
        <h2>电影列表</h2>
        <ul>
          <li>{this.state.movies[0]}</li>
          <li>{this.state.movies[1]}</li>
          <li>{this.state.movies[2]}</li>
        </ul>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

显然,目前这种静态展示的方法并不理想。我们最好对 movies 数组进行遍历展示。遍历的方式有多种,这里先介绍以下两种常用方式。

方式一:对 movies 数组进行遍历,将每个元素放入一个新数组中,再渲染该数组。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      movies: ["大话西游", "黑话律师", "独行月球"]
    }
  }

  render() {
    const liEls = []
    for (let item of this.state.movies) {
      const liEl = <li key={item}>{item}</li>
      liEls.push(liEl)
    }

    return (
      <div>
        <h2>电影列表</h2>
        <ul>
          {liEls}
        </ul>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

方式二:使用 map 方法直接生成一个新数组,并将这个数组直接放入 ul 元素中进行渲染。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      movies: ["大话西游", "黑话律师", "独行月球"]
    }
  }

  render() {

    return (
      <div>
        <h2>电影列表</h2>
        <ul>
          {this.state.movies.map(item => <li key={item}>{item}</li>)}
        </ul>  
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

计数器的案例

接下来,我们再使用 React 实现一个大家熟悉的计数器案例,以巩固所学知识。

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      conter: 0
    }

    // 为方法绑定this
    this.increment = this.increment.bind(this)
    this.decrement = this.decrement.bind(this)
  }

  increment() {
    this.setState({
      conter: this.state.conter + 1
    })
  }

  decrement() {
    this.setState({
      conter: this.state.conter - 1
    })
  }

  render() {
    return (
      <div>
        <h2>{this.state.conter}</h2>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
      </div>
    )
  }
}

const app = ReactDOM.createRoot(document.querySelector("#app"))
app.render(<App/>)

到此为止,关于 Hello React 的组件化方式及 React 入门小案例的介绍就到这里。更多相关 Hello React 案例内容,请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

作者最新文章