深入浅出 React 组件

93 阅读7分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

React 组件介绍

  1. 组件是 React 的一等公民,使用 React 就是在用组件。
  2. 组件表示页面中的部分功能。
  3. 组合多个组件实现完整的页面功能。
  4. 特点:
    • 可复用。
    • 独立性。
    • 可组合。

React 组件的两种创建方式

使用函数创建组件:

  1. 函数组件:使用 JS 的函数(或建瓯函数)创建的组件

    • 约定一:函数名称必须以大写字母开头。
    • 约定二:函数组件必须有返回值,表示该组件的结构。
    • 如果返回值是 null ,表示不渲染任何内容。
    function Hello () {
      return (
      	<div>这是我的第一个函数组件!</div>
      )
    }
    
  2. 渲染函数组件:用函数名作为组件标签名。

    • 组件标签可以是单标签也可以是双标签。
    function Hello () {
      return (
      	<div>这是我的第一个函数组件!</div>
      )
    }
    
    // 渲染
    ReactDOM.render(<Hello />, document.querySelector('#root')
    
    • 用箭头函数创建组件:
    const Hello = () => <div>这是我的第一个函数组件</div>
    
    // 这个渲染的效果等同于上面的那个函数创建的组件。
    
  3. 总结:

    • 使用 JS 中的函数创建的组件叫做:函数组件
    • 函数组件必须有返回值。
    • 组件名称必须以大写字母开头,React 据此区分 组件 和 普通的 React 元素。
    • 使用函数名作为组件标签名。

使用类创建组件:

  1. 类组件:使用 ES6 的 class 创建的组件。

    • 约定一:类名称也必须以大写字母开头。
    • 约定二:类组件应该继承 React.Component 父类,从而可以使用父类中提供的方法或属性。
    • 约定三:类组件必须提供 render() 方法。
    • 约定四:render() 方法必须有返回值,表示该组件的结构。
    class Hello extends React.Component {
      render () {
        return <div>Hello Class Component!</div>
      }
    }
    ReactDOM.render(<Hello />, document.querySelector('#root')
    

抽离为独立 JS 文件

  1. 思考:项目中的组件多了以后,该如何组织这些组件呢?

    • 选择一:将所有组件放在同一个 JS 文件中。
    • 选择二:将每个组件放到单独的 JS 文件中。
  2. 组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中

  3. 抽离组件的步骤:

    • 创建 Hello.js 文件。
    • 在 Hello.js 中导入 React 。
    • 创建组件(函数组件或类组件)。
    • 在 Hello.js 中导出该组件。
    • 在 index.js 中导入 Hello 组件。
    • 渲染组件。
    // Hello.js 文件
    import React from 'react'
    class Hello extends React.Component {
      render () {
        return <div>Hello Class Component!</div>
      }
    }
    
    // 导出 Hello 组件
    export default Hello
    
    // index.js 文件
    import Hello from './Hello'
    
    // 渲染导入的 Hello 组件
    ReactDOM.render(<Hello />, document.querySelector('#root')
    

React 事件处理

事件绑定

  1. React 事件绑定语法与 DOM 事件语法相似。

  2. 语法:

    • on + 事件名称 = {事件处理程序}

    • 比如:onClick={() => {}}

  3. 注意:**React 事件采用驼峰命名法,比如:onMouseEnter、onFocus 。

    // 通过类创建组件
    class App extends React.Component {
      handleClick() {
        console.log('单击事件触发了')
      }
      render() {
        return (
        	<button onClick={this.handleClick}>点我</button>
        )
      }
    }
    
    // 通过函数创建组件
    function App () {
      function handleClick() {
        console.log('单击事件触发了')
      }
      return (
        	<button onClick={this.handleClick}>点我</button>
        )
    }
    

事件对象

  1. 可以通过事件处理程序的参数获取到事件对象。

  2. React 中的事件对象叫做:合成事件(对象)

  3. 合成事件:兼容所有浏览器,无需担心跨域浏览器兼容性问题。

    function handleClick(e) {
      e.preventDefault()
      console.log('事件对象',e)
    }
    <a onClick={handleClick}>点我不会跳转页面</a>
    
  4. 具体使用:

    function App() {
      function handleClick(e) {
        // 阻止浏览器的默认行为
        e.preventDefault()
        console.log('事件对象',e)
      }
      return(
        <a href="www.baidu.com" onClick={handleClick}>点击页面不跳转</a>
      )
    }
    
    // 渲染react元素
    ReactDOM.render(<App />, document.querySelector('#root'))
    

有状态组件和无状态组件

  1. 函数组件又叫做无状态组件,类组件又叫做有状态组件
  2. 状态(state)即数据
  3. 函数组件没有自己的状态,只负责数据展示(静)。
  4. 类组件有自己的状态,负责更新 UI ,让页面“动”起来。
    • 比如计数器案例中,点击按钮让数值加1.0和1就是不同时刻的状态,而由0变1就表示状态发生了变化。状态变化后,UI 也要相应的更新。React 中想要实现该功能,就要使用有状态组件来完成。

组件中的 state 和 setState

state 的基本使用

  1. 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用。

  2. state 的值是对象,表示一个组件中可以有多个数据。

    class App extends React.Component {
      constyuctor() {
        super()
        // 初始化 state
        this.state = {
          count: 0
        }
      }
      
      // 简化写法(推荐)
      state = {
        count: 0
      }
      
      render(){
        return (
        	<div>
          	<h1>计数器:{this.state.count}</h1>
          </div>
        )
      }
    }
    
    ReactDOM.render(<App />, document.querySelector('#root'))
    
  3. 总结:

    • 状态即数据。
    • 状态是私有的,只能在组件内部使用。
    • 通过 this.state 来获取状态。

setState() 修改状态

  1. 状态是可变的。

  2. 语法:

    • this.setState({要修改的数据})
  3. 注意:不要直接修改 state 中的值,这是错误的!!!

    // 正确
    this.setState({
      count: this.state.count + 1
    })
    
    // 错误
    this.state.count += 1
    
  4. setState() 的作用:

    • 修改 state 。
    • 更新 UI 。
  5. 思想:数据驱动视图

从 JSX 中抽离事件处理程序

  1. JSX 中掺杂过多 JS 逻辑代码,会显得非常混乱。

  2. 推荐:将逻辑抽离到单独的方法中,保证 JSX 结构清晰。

在这里插入图片描述

  1. 原因:事件处理程序中 this 的值为 undefined 。

  2. 希望:this 指向组件实例(render 方法中的 this 即为组件实例)

事件绑定 this 指向

箭头函数

  1. 利用箭头函数自身不绑定 this 的特点。

  2. render() 方法中的 this 为组件实例,可以获取到 setState() 。

    class App extends React.Component {
      state = {
        count: 0
      }
      onIncrement() {
        this.setState({
          count: this.state.count + 1
        })
      }
      render() {
        // 箭头函数中的 this 指向外部环境,此处为:render() 方法。
        return (
          <div>
            <h1>总数:{this.state.count}</h1>
            <button onClick={() => this.onIncrement()}>+1</button>
          </div>
        )
      }
    }
    

Function.prototype.bind()

  1. 利用 ES5 中的 bind 方法,将事件处理程序中的 this 与组件实例绑定到一起。

    class App extends React.Component {
    	constructor() {
      	super()
      	this.state = {
        	count: 0
      	}
      	this.onIncrement = this.onIncrement.bind(this)
    	}
      onIncrement() {
        this.setState({
          count: this.state.count + 1
        })
      }
      render() {
        // 箭头函数中的 this 指向外部环境,此处为:render() 方法。
        return (
          <div>
            <h1>总数:{this.state.count}</h1>
            <button onClick={this.onIncrement}>+1</button>
          </div>
        )
      }
    }
    

class 的实例方法

  1. 利用箭头函数形式的 class 实例方法。

  2. 注意:该语法是实验性语法,但是,由于 babel 的存在可以直接调用。

    class App extends React.Component {
      state = {
        count: 0
      }
      onIncrement = () => {
        this.setState({
          count: this.state.count + 1
        })
      }
      render() {
        return (
        	<div>
          	<h1>总数:{this.state.count}</h1>
          	<button onClick={this.onIncrement}>+1</button>
          </div>
        )
      }
    }
    

总结

  1. 推荐:使用 class 的实例方法。

    class Hello extends React.Component {
      onIncrement = () => {
        this.setState({...})
      }
    }
    
  2. 箭头函数。

    <button onClick={() => this.onIncrement()}>+1</button>
    
  3. bind

    constructor() {
      super()
      this.onIncrement = this.onIncrement.bind(this)
    }
    

表单处理

受控组件

  1. HTML 中的表单元素是可输入的,也就是有自己的可变状态。

  2. 而 React 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改。

  3. React 将 state 与表单元素的 value 绑定到了一起,由 state 的值来控制表单元素的值

    <input type="text" value={this.state.txt} />
    
    
  4. 受控组件:其值受到 React 控制的表单元素。

  5. 步骤:

    • 在 state 中添加一个状态,作为表单元素的 value 值(控制表单元素值的来源)。

      state = { txt: '' }
      
      
      <input type="text" value={this.state.txt} />
      
    • 给表单元素绑定 change 事件,将表单元素的值设置为 state 的值(控制表单元素值的变化)。

      <input type="text" value={this.state.txt} onChange={e => this.setState({ txt: e.target.value })} />
      
  6. 实例:

    • 文本框
    class App extends React.Component {
      state = {
        txt: ''
      }
      handleChange = e => {
        this.setState({
          txt: e.target.value
        })
      }
      render() {
        return (
          <div>
            <input type="text" value={this.state.txt} onChange={this.handleChange} />
            <h2>{this.state.txt}</h2>
          </div>
        )
      }
    }
    
    • 富文本框、下拉框
    class App extends React.Component {
      state = {
        content: ''
      }
      handleContent = e => {
        this.setState({
          content: e.target.value
        })
      }
      render() {
        return (
          <div>
            <input type="text" value={this.state.content} onChange={this.handleContent} />
            <h2>{this.state.content}</h2>
          </div>
        )
      }
    }
    
    • 下拉框
    class App extends React.Component {
      state = {
        city: 'sh'
      }
      handleCity = e => {
        this.setState({
          city: e.target.value
        })
      }
      render() {
        return (
          <div>
            <select value={this.state.city} onChange={this.handleCity}>
            	<option value="bj">北京</option>
              <option value="sh">上海</option>
              <option value="gz">广州</option>
            </select>
            <h2>{this.state.city}</h2>
          </div>
        )
      }
    }
    
    
    • 复选框
    class App extends React.Component {
      state = {
        isChecked: true
      }
      handleChecked = e => {
        this.setState({
          isChecked: e.target.checked
        })
      }
      render() {
        return (
          <div>
            <input type="checkbox" checked={this.state.isChecked} onChange={this.handleChecked} />
          </div>
        )
      }
    }
    
    

示例总结

  1. 文本框、富文本框、下拉框操作 value 属性。
  2. 复选框操作 checked 属性。

多表单元素优化

  1. 问题:每个表单元素都有一个单独的事件处理程序处理太繁琐。

    • 优化:使用一个事件处理程序同时处理多个表单元素。
  2. 步骤:

    • 给表单元素添加 name 属性,名称与 state 相同。

      <input
        type="text"
        name="txt"
        value={this.state.txt}
        onChange={this.handleForm}
      />
      
    • 根据表单元素类型获取对应值。

      // 根据表单元素类型获取值
      const value = target.type === 'checkbox' ? target.checked : target.value
      
      // 根据 name 设置对应 state
      this.setState({
        [name]: value
      })
      
    • 在 change 事件处理程序中通过 [name] 来修改对应的 state 。

  3. 实例:

    class App extends React.Component {
      state = {
        txt: '',
        content: '',
        city: 'sh',
        isChecked: true
      }
      handleForm = e => {
        // 获取当前 DOM 对象
        const target = e.target
        // 根据类型获取值
        const value = target.type === 'checkbox' ? target.checked : target.value
        // 获取 name
        const name = target.name
        this.setState({
          [name]: value
        })
      }
      render() {
        return (
          <div>
            <input name="txt" type="text" value={this.state.txt} onChange={this.handleForm} />
            <br/>
            <h1>{this.state.txt}</h1>
            <textarea name="content" type="checkbox" checked={this.state.content} onChange={this.handleForm} />
            <br/>
            <h1>{this.state.content}</h1>
            <select name="city" value={this.state.city} onChange={this.handleForm}>
              <option value="bj">北京</option>
              <option value="sh">上海</option>
              <option value="gz">广州</option>
            </select>
            <br/>
            <h1>{this.state.city}</h1>
            <input name="isChecked" type="checkbox" checked={this.state.isChecked} onChange={this.handleForm} />
            <br/>
            <h1>{this.state.isChecked}</h1>
          </div>
        )
      }
    }
    

非受控组件

  1. 使用步骤:

    • 调用 React.createRef() 方法创建一个 ref 对象。

      constructor() {
        super()
        this.txtRef = React.createRef()
      }
      
    • 将创建好的 ref 对象添加到文本框中。

      <input type="text" ref={this.txtRef} />
      
    • 通过 ref 对象获取到文本框的值。

      console.log(this.txtRef.current.value)
      
    • 大多数情况下使用受控组件。

总结

  1. 组件的两种创建方式:函数组件和类组件。
  2. 无状态(函数)组件,负责静态结构展示。
  3. 有状态(类)组件,负责更新 UI ,让页面动起来。
  4. 绑定事件注意 this 指向。
  5. 推荐使用受控组件来处理表单。
  6. 完全利用 JS 语言的能力创建组件,这是 React 的思想。