React学习[二]
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'))
表单元素优化
问题:每个表单元素都有一个单独的事件处理程序处理太繁琐
优化:使用一个事件处理程序同时处理多个表单元素
多表单元素优化步骤:
- 给表单元素添加name属性,名称与state相同
- 根据表单元素类型获取对应值
- 在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'))