目录
React组件实例的三大核心属性
state
- state 是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
- 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
即:组件—>状态—>驱动—>页面
初始化state
1.创建一个有状态的组件
<body>
<div id="test"></div>
<script type="text/babel">
// 1.创建组件
class Weather extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = { isHot: true };
}
render() {
// 解构赋值
const {isHot} = this.state;
return <h1>今天天气很{isHot ? "炎热" : "寒冷"}</h1>
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, document.getElementById("test"))
</script>
</body>
React中事件绑定
- 响应事件函数名采用驼峰命名法:
onClick、onBlur
等- 用法:
<h1 onClick={回调函数}></h1>
render() {
// 解构赋值
return <h1 onClick={this.changeWeather}></h1>
}
changeWeather() {
console.log('今天天气真好!');
}
错误写法:
// 错误写法一
<h1 onClick='this.changeWeather'></h1>
// 错误写法二
<h1 onClick={this.changeWeather()}></h1>//这种写法会直接执行,相当于直接调用
类组件中自定义方法中this
问题
- 通过组件实例调用的方法中的
this
是组件实例对象- 组件中
render
方法中的this
为组件实例对象,render
方法是由组件实例对象调用的- 在类组件中的函数方法系统默认都默认开启了局部严格模式
- 组件
自定义方法
会放在组件对象的原型上,供组件实例使用- 在类组件中除了
render()
函数以外,自定义方法
一般是作为事件的回调,所以不是通过实例调用的,是直接调用,又因为类组件中的方法默认开启了局部的严格模式,所以个人在类组件写的函数方法中的this
是undefined
render() {
// 解构赋值
const { isHot } = this.state;
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? "炎热" : "寒冷"}</h1>
}
changeWeather() {
console.log(this);// undefined
}
解决类中this指向问题
- 强制绑定
this
:通过函数对象的bind()
- 在构造器中绑定
this
,this.changeWeather = this.changeWeather.bind(this)
<script type="text/babel">
// 1.创建组件
class Weather extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = { isHot: true };
// 解决changeWeather中this指向问题
this.changeWeather = this.changeWeather.bind(this)
}
render() {
// 解构赋值
const { isHot } = this.state;
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? "炎热" : "寒冷"}</h1>
}
changeWeather() {
// changeWeather放在哪里? ———— Weather的原型对象上,供组件实例使用
// 由于changeWeather是作为onClick的回调,所以不是通过实例调用,是直接调用
// 类中的方法默认开启了局部的严格模式
console.log(this);// undefined
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, test)
</script>
- 在回调函数中绑定
this
<script type="text/babel">
// 1.创建组件
class Weather extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = { isHot: true };
// 解决changeWeather中this指向问题
// this.changeWeather = this.changeWeather.bind(this)
}
render() {
// 解构赋值
const { isHot } = this.state;
// 解决changeWeather中this指向问题
return <h1 onClick={this.changeWeather.bind(this)}>今天天气很{isHot ? "炎热" : "寒冷"}</h1>
}
changeWeather() {
console.log(this);// undefined
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, test)
</script>
- 使用箭头函数解决
this
指向问题
<script type="text/babel">
// 1.创建组件
class Weather extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = { isHot: true };
// 方法1:解决changeWeather中this指向问题
// this.changeWeather = this.changeWeather.bind(this)
}
render() {
// 解构赋值
const { isHot } = this.state;
// 方法2:解决changeWeather中this指向问题
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? "炎热" : "寒冷"}</h1>
}
// 方法:3:解决changeWeather中this指向问题
changeWeather = () => {
console.log(this);// undefined
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, test)
</script>
通过setState方法修改状态(state)
- 状态(state)不可直接更改,需要使用内置API的setState方法去更新
- setState方法的参数是一个对象
- 传入的对象会和原来的状态进行合并,如果同名就直接覆盖值
- 每次setState方法修改状态(state)就会调动一次
render()
方法
<script type="text/babel">
// 1.创建组件
class Weather extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = { isHot: true };
// 方法1:解决changeWeather中this指向问题
// this.changeWeather = this.changeWeather.bind(this)
}
render() {
// 解构赋值
const { isHot } = this.state;
// 方法2:解决changeWeather中this指向问题
return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? "炎热" : "寒冷"}</h1>
}
// 方法:3:解决changeWeather中this指向问题
changeWeather = () => {
const isHot = this.state.isHot;
// 更新状态
this.setState({
isHot: !isHot
})
console.log(this.state.isHot);// undefined
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, test)
</script>
state的简写代码
// 1.创建组件
class Weather extends React.Component {
// 初始化状态
state = { isHot: true };
render() {
const { isHot } = this.state;
return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? "炎热" : "寒冷"}</h1>
}
// 自定义方法
changeWeather = () => {
const isHot = this.state.isHot;
this.setState({
isHot: !isHot
})
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, test)
props
- 每个组件实例对象都会有
props
属性- 组件标签的属性都保存在
props
中- 组件中的
props
是只读不可以通过this.props.属性名 ="属性"
的方式去修改,会直接报错
props的基本使用
可以直接进行传参
<body>
<div id="test"></div>
<script type="text/babel">
class Person extends React.Component{
render(){
console.log(this.props);
return (
<ul>
<li>姓名:{this.props.name}</li>
<li>性别:{this.props.sex}</li>
<li>年龄:{this.props.age}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="江流" age={19} sex="男"/>, document.getElementById("test"))
</script>
</body>
批量传递props
使用构造字面量对象是使用展开语法
<Person {...p}/>
class Person extends React.Component {
render() {
console.log(this.props);
return (
<ul>
<li>姓名:{this.props.name}</li>
<li>性别:{this.props.sex}</li>
<li>年龄:{this.props.age}</li>
</ul>
)
}
}
const p = { name: "江流", age: 19, sex: "男" }
ReactDOM.render(<Person {...p}/>, document.getElementById("test"))//这里的{...p}不是使用的扩展运算符,是在babel环境下传参的一种方式,只能在标签传参里面使用
props进行类型限制
- 需要prop-types库,对标签属性进行类型、必要性的限制
- 通过给组件上的添加并设置
propTypes
属性来限制props
的类型,如果传入的prop类型有错误,React则会报出警告- 通过给组件上的添加并设置
defaultProps
属性来设置props
的默认值
<script type="text/babel">
class Person extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired, //限制name必传,且为字符串
sex: PropTypes.string, //限制sex为字符串
age: PropTypes.number, //限制age为数值
speak: PropTypes.func, //限制speak为函数
}
static defaultProps = {
sex: "男", //sex默认值为男
age: 18, //age默认值为18
}
render() {
console.log(this.props);
return (
<ul>
<li>姓名:{this.props.name}</li>
<li>性别:{this.props.sex}</li>
<li>年龄:{this.props.age}</li>
</ul>
)
}
}
function speak() {
console.log("我是谁?");
}
const speak2 = 1;
ReactDOM.render(<Person name="江流儿" speak={speak} />, document.getElementById("test"))// 这里的...不是构造字面量
</script>
props与构造器
constructor(props){
super(props)
}
通常React中,构造函数仅仅用于以下两种情况
- 通过给
this.state
赋值对象来初始化内部state
- 为事件处理函数绑定实例(使用bind函数)
- 在React组件挂载之前,会调用它的构造函数。在实现构造函数时,必须在其他语句之前调用super(props).否则,
this.props
在构造函数中会出undefined
- 构造器中是否接收
props
,是否传递给super
,取决于:是否希望在构造器中通过this
访问props
refs
在正常操作DOM真实节点时,需要采用DOM API来查找元素,但是这样违背了React的不直接操作DOM的理念,因此React提供了refs来定位虚拟DOM来操作真实DOM
组件内的标签可以定义ref属性来标识自己
有三种操作refs
的方法,分别为:
- 字符串形式的
ref
(过时的API)- 回调形式
ref
createRef()
形式请勿过度使用Refs
字符串形式的ref
React官网不建议使用该方法
class Demo extends React.Component {
showData = () => {
const { input1 } = this.refs;
alert(input1.value);
}
render() {
return (
<div>
<input type="text" ref='input1' />
<button onClick={this.showData}>点我</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
回调形式ref
通过给ref属性传递一个回调函数
ref={currentNode => this.input1 = currentNode}
(箭头函数简写)来创建一个ref
class Demo extends React.Component {
showData1 = () => {
const { input1 } = this;
alert(input1.value);
}
showData2 = () => {
const { input2 } = this;
alert(input2.value);
}
render() {
console.log(this.refs);
return (
<div>
<input type="text" ref={currentNode => this.input1 = currentNode} />
<button onClick={this.showData1}>点我</button>
<input type="text" ref={currentNode => this.input2 = currentNode} />
<button onClick={this.showData2}>点我</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
回调refs的说明
- 如果ref回调是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null,然后第二次才会传入参数DOM元素。这是因为每次渲染是会创建一个新的函数实例,所以React清空旧的ref并且设置新的。通过ref的回调定义成class的绑定函数可以避免上诉问题,但是大多数情况下是无关紧要的
createRef形式
该形式是
React
官方推荐使用的方式
React.createRef
方法调用后可以返回一个容器,该容器可以储存被ref
所标识的节点
该容器是“专人专用”的,容器中仅有一个属性current
,属性值即存放的节点
class Demo extends React.Component {
myRef1 = React.createRef();
myRef2 = React.createRef();
showData1 = () => {
alert(this.myRef1.current.value);
}
showData2 = () => {
alert(this.myRef2.current.value);
}
render() {
console.log(this.refs);
return (
<div>
<input type="text" ref={this.myRef1} />
<button onClick={this.showData1}>点我</button>
<input type="text" ref={this.myRef2} />
<button onClick={this.showData2}>点我</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
事件处理
- 通过
onXxx
属性指定事件处理函数,例如onClick
(注意大小写)
a. React使用的是自定义(合成)事件,而不是使用原生DOM事件 —— 为了更好的兼容性
b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——— 为了更好的高效- 通过event.target也可以的得到发生事件的DOM元素对象 —— 不要过渡使用ref