React学习[二]

React组件介绍

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

组件的创建方式

使用函数创建组件

  • 函数组件:使用JS的函数创建组件
  • 约定1:函数名称必须以大写字母开头,React 据此区分组件和普通的React元素
  • 约定2:函数组件必须有返回值,表示该组件的结构
  • 如果返回值为null,表示不渲染任何内容
function Hello(){
	return (
		<div>第一个函数组件</div>
	)
}

渲染函数组件:用函数名作为组件标签名
组件标签可以是单标签也可以是双标签

// function Hello() {
//   return (
//     <div> 第一个组件 </div>
//   )
// }
// 箭头函数
const Hello = () => <div> 第一个函数组件 </div>
// 渲染组件
ReactDOM.render(< Hello />, document.getElementById('root'))

使用类创建组件

类组件:使用ES6的class创建的组件
约定1:类名称也必须以大写字母开头
约定2:类组件应该继承React.Component父类,从而可以使用父类中提供的方法或属性
约定3:类组件必须提供render()方法
约定4:render()方法必须有返回值,表示该组件的结构

// 创建类组件
class Hello extends React.Component {
	render(){
		return (
			<div> 第一个类组件 </div>
		)
	}
}
// 渲染组件
ReactDOM.render(< Hello />, document.getElementById('root'))

抽离为单独JS文件

组件作为一个独立的个体,一般都会放到一个单独的JS文件中。
步骤:

  • 创建Hellojs
  • 在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.getElementById('root'))

事件对象

事件绑定

语法:on+事件名称={事件处理程序},比如onClick = {()=>{}}
注意:React事件采用驼峰命名法,比如:onMouseEhter、onFocus

// // 函数事件
// function App() {
//   function handleClick() {
//     console.log('类组件中的事件绑定,单击事件触发了')
//   }
//   return (
//     <button onClick={handleClick}>函数事件绑定</button>
//   )
// }
// 类事件
class App extends React.Component {
  handleClick() {
    console.log('函数组件中的事件绑定,单击事件触发了')
  }
  render() {
    return (
      <button onClick={this.handleClick}>类事件绑定</button>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

事件对象

  • 可以通过事件处理函数的参数获取到事件对象
  • React中的事件对象叫做:合成事件
  • 合成事件:兼容所有浏览器,无需担心跨浏览器兼容问题
  • 除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation()preventDefault()
  • 如果你想获取到原生事件对象,可以通过 nativeEvent 属性来进行获取
// 类事件
class App extends React.Component {
  // 事件处理程序
  handleClick(e) {
    e.preventDefault()
    console.log('a标签的单击事件触发了')
  }
  render() {
    return (
      <a href="http://itcast.cn/" onClick={this.handleClick}>传智播客</a>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

有状态组件和无状态组件

  • 函数组件又叫做无状态组件,类组件又叫做有状态组件
  • 状态(state) 即数据
  • 函数组件没有自己的状态,只负责数据展示(负责静态结构展示)
  • 类组件有自己的状态,负责更新UI,让页面动起来

state的基本使用

状态( state )即数据,是组件内部的私有数据,只能在组件内部使用
state的值是对象,表示一个组件中可以有多个数据

class App extends React.Component {
  // // 初始化状态
  // constructor() {
  //   super();
  //   // 初始化state
  //   this.state = {
  //     count: 0
  //   }
  // }

  // 简化语法
  state = {
    count: 0
  }
  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

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

setState()修改状态

状态是可变的
语法:this.setState({要修改的数据)
注意:不要直接修改state中的值,这是错误的! ! !
setState()作用:1.修改state; 2.更新UI
思想:数据驱动视图

/* 
  setState()的基本使用
*/
class App extends React.Component {
  // 简化语法
  state = {
    count: 0
  }
  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={() => {
          this.setState({
            count: this.state.count + 1
          })
          // 错误演示
          // this.state.count += 1
        }}>+1</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

事件绑定this指向问题

问题:
使用JSX抽离单独的程序处理时,this为undefined

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>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

箭头函数

利用箭头函数自身不绑定this的特点
render()方法中的this为组件实例,可以获取到setState()

class App extends React.Component {
  // 简化语法
  state = {
    count: 0
  }
  // 事件处理程序
  onIncrement() {
    this.setState({ // 这里的this指向下面箭头函数的this
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1> 计数器: {this.state.count} </h1>
        <button onClick={() => this.onIncrement()} > +1 </button>
        {/* () => this.onIncrement()才是真正的事件处理程序
          这里的this就是所求实例
        */}
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

Function.prototype.bind()

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

class App extends React.Component {
  // 简化语法
  state = {
    count: 0
  }
  constructor() {
    super()
    this.onIncrement = this.onIncrement.bind(this)
  }
  // 事件处理程序
  onIncrement() {
    this.setState({ // 这里的this指向下面箭头函数的this
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1> 计数器: {this.state.count} </h1>
        <button onClick={this.onIncrement} > +1 </button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

class实例方法

利用箭头函数形式的class实例方法
注意:该语法是实验性语法,但是,由于babel的存在可以直接使用

class App extends React.Component {
  // 简化语法
  state = {
    count: 0
  }
  // 事件处理程序
  onIncrement = () => {
    this.setState({ // 这里的this指向下面箭头函数的this
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1> 计数器: {this.state.count} </h1>
        <button onClick={this.onIncrement} > +1 </button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

表单处理

受控组件

HTML中的表单元素是可输入的,也就是有自己的可变状态
React中可变状态通常保存在state中,并且只能通过setState()方法来修改
React将state 与表单元素值value绑定到一起,由state的值来控制表单元素的值
受控组件:其值受到React控制的表单元素。
步骤

  • 在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
  • 给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)
class App extends React.Component {
  // 简化语法
  state = {
    txt: ''
  }
  // 事件处理程序
  handleChange = e => {
    this.setState({ // 这里的this指向下面箭头函数的this
      txt: e.target.value
    })
  }
  render() {
    return (
      <div>
        <input type="text" value={this.state.txt} onChange={this.handleChange} />
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

文本框、富文本框、下拉框操作value属性
复选框操作checked属性

class App extends React.Component {
  // 简化语法
  state = {
    txt: '',
    content: '',
    city: 'bj',
    isChecked: false
  }
  // 事件处理程序
  handleChange = e => {
    this.setState({ // 这里的this指向下面箭头函数的this
      txt: e.target.value
    })
  }
  // 处理富文本框的变化
  handleContentChange = e => {
    this.setState({
      content: e.target.value
    })
  }
  // 处理下拉框的变化
  handleCityChange = e => {
    this.setState({
      city: e.target.value
    })
  }
  // 处理复选框的变化
  handleCheckedChange = e => {
    this.setState({
      isChecked: e.target.checked
    })
  }
  render() {
    return (
      <div>
        {/* 文本框 */}
        <input type="text" value={this.state.txt} onChange={this.handleChange} />
        <br />

        {/* 富文本框 */}
        <textarea value={this.state.content} onChange={this.handleContentChange}></textarea>
        <br />

        {/* 下拉框 */}
        <select value={this.state.city} onChange={this.handleCityChange}>
          <option value="sh">上海</option>
          <option value="bj">北京</option>
          <option value="hz">杭州</option>
        </select>
        <br />

        {/* 复选框 */}
        <input type="checkbox" checked={this.state.isChecked} onChange={this.handleCheckedChange} />
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

表单元素优化

问题:每个表单元素都有一个单独的事件处理程序处理太繁琐
优化:使用一个事件处理程序同时处理多个表单元素
多表单元素优化步骤:

  1. 给表单元素添加name属性,名称与state相同
  2. 根据表单元素类型获取对应值
  3. 在change事件处理程序中通过[name]来修改对应的state
class App extends React.Component {
  // 简化语法
  state = {
    txt: '',
    content: '',
    city: 'bj',
    isChecked: false
  }
  // 事件处理程序
  handleForm = e => {
    // 获取当前DOM对象
    const target = e.target
    // 根据类型获取值
    const value = target.type === 'checkbox' ? target.checked : target.value
    // 获取name
    const name = target.name
    this.setState({ // 这里的this指向下面箭头函数的this
      [name]: value
    })
  }
  render() {
    return (
      <div>
        {/* 文本框 */}
        <input type="text" name="txt" value={this.state.txt} onChange={this.handleForm} />
        <br />

        {/* 富文本框 */}
        <textarea name="content" value={this.state.content} onChange={this.handleForm}></textarea>
        <br />
        {/* 下拉框 */}
        <select name="city" value={this.state.city} onChange={this.handleForm}>
          <option value="sh">上海</option>
          <option value="bj">北京</option>
          <option value="hz">杭州</option>
        </select>
        <br />

        {/* 复选框 */}
        <input type="checkbox" name="isChecked" checked={this.state.isChecked} onChange={this.handleForm} />
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

非受控组件

说明:借助于ref,使用原生DOM方式来获取表单元素值;
ref的作用:获取DOM或组件。
使用步骤:
1.调用React.createRef()方法创建一个 ref对象

constructor() {
	super()
	this.txtRef = React.createRef()
}

2.将创建好的ref对象添加到文本框中

<input type="text" ref={this.txtRef} />

3.通过ref对象获取到文本框的值

console.log (this.txtRef.current.value )
class App extends React.Component {
  constructor() {
    super()
    // 创建ref
    this.txtRef = React.createRef()
  }
  // 获取文本框的值
  getTXT = () => {
    console.log("文本框的值:", this.txtRef.current.value)
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.txtRef} />
        <button onClick={this.getTXT}>获取文本框的值</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

发表评论案例

需求:

  • 渲染评论列表(列表渲染)
    ①在state中初始化评论列表数据
    ②使用数组的map方法遍历state中的列表数据
    ③给每个被遍历的li元素添加key属性
  • 没有评论数据时渲染:暂无评论(条件渲染)
    ①判断列表数据的长度是否为0
    ②如果为0,则渲染暂无评论
  • 获取评论信息,包括评论人和评论内容(受控组件)
    ①使用受控组件方式处理表单元素
  • 发表评论,更新评论列表setState()
    ①给按钮绑定单击事件
    ②在事件处理程序中,通过state获取评论信息
    ③将评论信息添加到state中,并调用setState()方法更新state
    ④边界情况:清空文本框
    ⑤边界情况:非空判断
class Hello extends React.Component {
  // 初始化状态
  state = {
    comments: [
      /*  { id: 1, name: 'jack', content: '沙发! ! !' },
       { id: 2, name: 'rose', content: '板凳' },*/
      { id: 3, name: 'tom', content: '楼主好人' }
    ],
    // 评论人
    userName: '',
    //  评论内容
    userContent: ''
  }
  // 渲染评论列表
  renderList() {
    const { comments } = this.state
    if (comments.length === 0) {
      return <div className="no-comment">暂无评论, 快去评论吧~</div>
    }
    return (
      <ul> {
        comments.map(
          item => (
            <li key={item.id}>
              <h3>评论人: {item.name}</h3>
              <p>评论内容: {item.content}</p>
            </li>
          )
        )}
      </ul>
    )
  }
  // 处理表单元素值
  handleForm = e => {
    const { value, name } = e.target
    this.setState({
      [name]: value
    })
  }
  // 发表评论的处理程序
  addComments = () => {
    const { comments, userName, userContent } = this.state
    // console.log(userName, userContent)
    //非空校验
    if (userName.trim() === '' || userContent.trim() === '') {
      alert('请输入评论人和评论内容')
      return
    }
    const newComments = [{
      id: Math.random,
      name: userName,
      content: userContent
    }, ...comments]
    // console.log(newComments)
    //文本框的值如何清空? 要清空文本框只需要将其对应的state清空即可
    this.setState({
      comments: newComments,
      userName: '',
      userContent: ''
    })
  }
  render() {
    const { userName, userContent } = this.state
    return (
      <div>
        <div className="app">
          <div>
            <input
              className="user"
              type="text"
              placeholder="请输入评论"
              value={userName}
              name="userName"
              onChange={this.handleForm}
            />
            <br />
            <textarea
              className="content"
              cols="30" rows="10"
              placeholder="请输入评论内容"
              value={userContent}
              name="userContent"
              onChange={this.handleForm}
            />
            <br />
            <button onClick={this.addComments}>发表评论</button>
          </div>
          {/* 通过条件渲染决定渲染什么评论 */}
          {this.renderList()}
        </div>
      </div>
    )
  }
}
// 渲染组件
ReactDOM.render(<Hello />, document.getElementById('root'))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值