【React】组件基础(5)

100 阅读5分钟

一、组件的介绍

  1. 组件表示页面中的部分功能

  2. 组合多个组件实现完整的页面功能

  3. 特点

    1. 复用:当多个页面都需要使用同一个功能的时候,可以抽出一个组件,进行复用
    2. 独立:每个组件都是互相独立的,保证使用同一个组件的时候不会受到影响
    3. 组合:有什么功能组合什么组件,就像堆积木一样,组成一个完整功能

二、组件创建方式

1. 函数组件(无状态)

  1. 使用 JS 的函数(箭头函数)创建的组件
  2. 约定1:函数名称必须是以 大写字母开头,区分组件和普通 react 元素
  3. 约定2:函数组件 必须有返回值,返回一个 react 元素,表示该组件的结构
  4. 渲染函数组件:用函数名作为组件标签名
  5. 函数组件没有自己的状态,只负责数据展示(静)
// 创建挂载的根节点
const root = ReactDOM.createRoot(document.getElementById("root"))
// 创建函数组件
function Hello() {
  return <div>hello Word</div>
}
// 箭头函数方式
const Hello = ()=> <div>hello Word</div>// 渲染组件
root.render(<Hello />)

2. 类创建组件(有状态)

  1. 使用 Es6 的 class 创建组件
  2. 约定1:类名称必须是以 大写字母开头
  3. 约定2:类组件必须继承 React.Component 父类,从而使用父类中提供的方法或属性
  4. 约定3:类组件必须提供 render 方法,必须有返回值,返回一个 react 元素,表示该组件的结构
  5. 渲染类组件:用类名作为组件标签名
  6. 类组件有自己的状态,负责更新 UI,让页面动起来
// 创建挂载的根节点
const root = ReactDOM.createRoot(document.getElementById("root"))
// 创建类组件
class Hello extends React.Component {
  render() {
    return <div>hello Word</div>
  }
}
// 渲染组件
root.render(<Hello />)

3. 总结

  1. 数据发生变化,视图也相应的更新,使用 类组件(有状态)
  2. 页面内容不需要发生变化和用户交互,使用 函数组件(无状态)

三、抽离为独立 JS 文件

  1. 创建 Hello.js
  2. 在 Hello.js 中导入 react
  3. 创建组件(函数 或 类)
  4. 在 Hello.js 导出该组件
  5. 在 index.js 中导入 Hello 组件
  6. 渲染组件
// Hello.jsimport React from 'react';
​
// 创建类组件
class Hello extends React.Component {
  render() {
    // this.props.age 接收传递的参数
    return <div>hello Word,{this.props.age}</div>
  }
}
​
// 导出组件
export default Hello
// index.jsimport Hello from './hello';
// 渲染组件
// age='20' 传递给组件的参数
root.render(<Hello age='20' />)

四、状态

1. state

  1. 状态(state)即为数据,组件内部的 私有数据,只能组件内部使用
  2. state 的值是对象,表示一个组件中可以有多个数据
  3. 通过 this.state 获取状态
class Hello extends React.Component {
  constructor() {
    super()
​
    // 初始化 state
    this.state = {
      age: 20
    }
  }
  
  // 简化写法 (推荐)
  // state = {
  //   age: 19
  // }
  
  render() {
    return <h1>{this.state.age}</h1>
  }
}

2. setState

  1. 状态是可变的

  2. 语法:this.setState({要修改的值})

  3. 注意:不能直接修改 state 中的值,这是错误的!!

  4. 作用:

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

class Hello extends React.Component {
  // 简化写法
  state = {
    age: 19,
  }
​
  render() {
    return (
      <div>
        <h1>{this.state.age}</h1>
        // 错误写法
        // <button onClick={() => this.state.age++}>+1</button>
        
        // 正确写法
        <button onClick={() => this.setState({ age: this.state.age + 1 })}>
          +1
        </button>
      </div>
    )
  }
}

3. 无法使用 setState 的问题

  1. JSX 中掺杂过多的 JS 逻辑代码,会显得非常混乱
  2. 推荐:将逻辑代码抽离到单独的方法中,保证 JSX 结构清晰
  3. 报错:Cannot read properties of undefined (reading 'setState')
  4. 原因:因为 setState 是组件实例中的方法,但是事件处理程序的 this 的值为 undefined
  5. 希望:this 指向组件实例 (render方法中的this即为组件实例)
1. 方法1 (箭头函数)
class Hello extends React.Component {
  // 简化写法
  state = {
    age: 19,
  }
​
  // 方式1
  // add() {
    // console.log(this);
    // this.setState({ age: this.state.age++ })
  // }
  
  // 方式2 (推荐)
  add = () => {
    this.setState({ age: this.state.age++ })
  }
​
  render() {
    return (
      <div>
        // 方法1:
        // 利用箭头函数自身不绑定 this 的特点
        // 箭头函数的 this 指向函数外部 render 方法的 this,再去调用组件实例的 add 方法
        // <button onClick={() => this.add()}>+1</button>
        
        // 方法2:
        // <button onClick={() => this.add()}>+1</button>
      </div>
    )
  }
}
2. 方法2(bind)
class Hello extends Component {
  constructor() {
    super()
    this.state = {
      age: 18,
    }
    // 使用 bind 改变 this 指向,返回一个新的函数,使用时调用即可
    this.add = this.add.bind(this)
  }
​
  add() {
    console.log(this) // 组件实例
    this.setState({ age: this.state.age + 1 })
  }
​
  render() {
    return (
      <div>
        <h1>{this.state.age}</h1>
        <button onClick={this.add}>+1</button>
      </div>
    )
  }
}

五、事件处理

1. 事件绑定

  1. react 事件绑定语法与 DOM 事件语法像相似
  2. 语法:on + 事件名称 = {事件处理程序} ,比如 onClick = {()=>{}}
  3. 注意:react 事件采用驼峰命名法,比如:onMouseEnter、onFocus
// 类组件

class Hello extends React.Component {
  // 事件函数
  btn() {
    console.log('事件触发');
  }
  render() {
    return (
      <div>
        hello Word
        <br />
        <button onClick={this.btn}>点击</button>
	  </div >
    );
  }
}


// 函数组件

function Hello() {
  // 事件函数
  function btn() {
    console.log('事件触发');
  }
  return (
    <div> hello Word < br /> <button onClick={btn}>点击</button></div >
  );
}

2. 事件对象

  1. 可以通过 事件处理程序的参数 获取到事件对象
  2. react 中的事件对象叫做 合成事件(对象)
  3. 合成事件:兼容所有浏览器,能让 react 正常运行在浏览器中,无需担心跨浏览器兼容性问题
function Hello() {
  // 事件函数
  function btn(e) {
    // 阻住浏览器的默认行为
    e.preventDefault()
    console.log('事件触发', e);
  }
  return (
    <a href="www.baidu.com" onClick={btn}>跳转页面</a >
  );
}

六、表单处理

1. 受控组件(推荐)

  1. HTML 中表单元素是可输入的,有自己的可变状态
  2. react 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改
  3. react 将 state 与表单 value 绑定到了一起,由 state 的值来控制表当元素的值
  4. 受控组件:其值受到 react 控制的表单元素

实例

class Hello extends React.Component {
  state = {
    // 1. state 中添加一个状态,做为表单的 value 值(控制表单值的来源)
    txt: 18,
  }

  // 2. 给表单添加 change 事件,将表单的值设为 state 的值(控制表单值的变化)
  update = e => {
    console.log(e)
    this.setState({ txt: e.target.value })
  }

  render() {
    return (
      <div>
        <h1>{this.state.txt}</h1>
        <input name="txt" onChange={this.update} value={this.state.txt} />
      </div>
    )
  }
}

多表单优化

class Hello extends React.Component {
  state = {
    txt: 18,
    check: true,
  }

  // 多表单优化
  update = (e) => {
    // 2. 获取 name 属性
    const name = e.target.name
    // 3. 根据表单类型获取值
    const value =
      e.target.type === 'checkbox' ? e.target.checked : e.target.value
    // 4. 根据 name 找到对应的属性设置 state
    this.setState({ [name]: value })
  }

  render() {
    return (
      <div>
        <h1>{this.state.txt}</h1>
        <h1>{this.state.check ? 1 : 0}</h1>
        // 1. 给表单添加 name 属性,名称和 state 相同
        // 文本框
        <input name="txt" onChange={this.update} value={this.state.txt} />
        <br />
        // 复选框
        <input
          name="check"
          type="checkbox"
          onChange={this.update}
          checked={this.state.check}
        />
      </div>
    )
  }
}

2. 非可控组件

  1. ref :获取 DOM 或 组件
  2. 借助 ref,使用原生 DOM 方式来获取表单元素
class Hello extends React.Component {
  constructor() {
    super()
    // 1. 通过 React.createRef() 创建一个 ref 对象
    this.txtRef = React.createRef()
  }

  getTxt = () => {
    // 3. 通过 ref 对象获取文本框的值
    console.log('文本框的值:',this.txtRef.current.value)
  }

  render() {
    return (
      <div>
        // 2. 将创建好的 ref 对象添加到文本框中
        <input ref={this.txtRef} onChange={this.update} />
        <button onClick={this.getTxt}>获取文本框的值</button>
      </div>
    )
  }
}