react中组件实例的三大核心属性
1、state
1.1、 理解:
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合
- 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
1.2、案例
** 需求:**定义一个展示天气信息的组件
- 默认展示天气炎热 或 凉爽
- 点击文字切换天气
代码示例:
<body>
<div id="test"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<script type="text/babel">
//1、创建组件
class Weather extends React.Component {
//构造器调用几次?----1次
constructor(props) {
super(props);
//初始化状态
this.state = {isHot: false, wind: '微风'};
//解决this指向问题
this.demo = this.changeWeather.bind(this);
}
//render调用几次?----1+n次,1是初始化那次,n是状态更新的次数
render() {
console.log(this);
const {isHot, wind} = this.state
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//changeWeather调用几次?---点几次调几次
changeWeather() {
//changeWeather放在了哪里?放在了类的原型对象上,供实例应用
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用的
//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
console.log(this);//在constructor中修改了this的指向后,this指向实例对象
//获取原来的isHot的值
let isHot = this.state.isHot
//状态不可直接更改,要借助一个内置的API更改,setState
this.setState({isHot: !isHot});
}
}
//2、渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'));
</script>
</body>
运行结果:
上述代码的简写:
<script type="text/babel">
// 1、创建类组件
class Weather extends React.Component {
//初始化状态
state = {isHot: false, wind: '微风'};
//自定义方法--赋值语句的形式+箭头函数
changeWeather = () => {
let isHot = this.state.isHot
this.setState({isHot: !isHot});
}
render() {
const {isHot, wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
}
//2、渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'));
</script>
1.3、注意:
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
a) 在构造函数中强制绑定this: 通过函数对象的bind()constructor(props) { super(props); this.demo = this.changeWeather.bind(this); }
b) 使用箭头函数(赋值语句+箭头函数的形式)changeWeather = () => { }
c)在render中绑定thisreturn <h1 onClick={this.changeWeather.bind(this)}>xxxxx</h1>
d)在render中使用箭头函数return <h1 onClick={()=>this.changeWeather()}>xxxxx</h1>
- 状态数据,不能直接修改或更新必须通过setState修改,且更新是一种合并,不是替换。setState是异步的
2、props
2.1、理解:
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
2.2、作用:
- 通过标签属性从组件外向组件内传递变化的数据
- 注意: 组件内部不要修改props数据
2,3、案例:
需求: 自定义用来显示一个人员信息的组件
- 姓名必须指定,且为字符串类型;
- 性别为字符串类型,如果性别没有指定,默认为男
- 年龄为字符串类型,且为数字类型,默认值为18
方法1:通过类式组件实现
<body>
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<!--引入prop-types,用于对组件标签属性进行限制-->
<script src="../js/prop-types.js"></script>
<script type="text/babel">
// 创建组件
class Person extends React.Component {
//通过静态属性static,将限制加在类本身,而不是实例对象上
//对标签属性进行类型,必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,//限制name必传且为字符串
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func//限制speak为函数
}
//指定标签属性默认值
static defaultProps = {
sex: '不男不女',
age: 18
}
render() {
//props是只读的
// this.props.name='张三李四';//这样会报错
const {name, age, sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age + 1}</li>
<li>性别:{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="小王" sex="男" speak={speak}></Person>, document.getElementById('test'))
ReactDOM.render(<Person name="小李" age={28} sex="女"/>, document.getElementById('test1'))
//批量传递属性
const p = {name: "小赵", age: 16}
ReactDOM.render(<Person {...p}/>, document.getElementById('test2'))
function speak() {
console.log('我说哈了');
}
</script>
</body>
方法2:通过函数式组件实现
<body>
<div id="test1"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<!--引入prop-types,用于对组件标签属性进行限制-->
<script src="../js/prop-types.js"></script>
<script type="text/babel">
// 1、创建组件
function Person(props) {
const {name, age, sex} = props;
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age + 1}</li>
<li>性别:{sex}</li>
</ul>
)
}
//通过静态属性static,将限制加在类本身,而不是实例对象上
//对标签属性进行类型,必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired,//限制name必传且为字符串
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func//限制speak为函数
}
//指定标签属性默认值
Person.defaultProps = {
sex: '不男不女',
age: 18
}
ReactDOM.render(<Person name="小李"/>, document.getElementById('test1'))
</script>
</body>
运行结果:
注意:
- props只允许读,不允许改
3、ref
3.1、理解
组件内的标签可以定义ref属性来标识自己
3.2、案例
需求: 自定义组件, 功能说明如下:
- 点击按钮, 提示第一个输入框中的值
- 当第2个输入框失去焦点时, 提示这个输入框中的值
实现请查看 ref使用方式
3.3、使用方式
3.3.1、字符串形式的ref
注意:不推荐使用,它已过时并可能会在未来的版本被移除
//创建组件
class Demo extends React.Component {
showDate = () => {
const {input1} = this.refs
alert(input1.value)
}
showDate2 = () => {
const {input2} = this.refs
alert(input2.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showDate}>点击我提示左侧的数据</button>
<input ref="input2" onBlur={this.showDate2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
3.3.2、回调形式的ref
//创建组件
class Demo extends React.Component {
showDate = () => {
const {input1} = this
alert(input1.value)
}
showDate2 = () => {
const {input2} = this
alert(input2.value)
}
render() {
return (
<div>
{/*this指向创建的这个实例对象*/}
<input ref={(currentNode)=>{this.input1=currentNode}} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showDate}>点击我提示左侧的数据</button>
<input ref={(currentNode)=>{this.input2=currentNode}} onBlur={this.showDate2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'));
关于回调refs调用几次的问题:
如果 ref 回调函数是以内联函数
的方式定义的,在更新
过程中它会被执行两次
,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
案例
<body>
<div id="test"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<script type="text/babel">
//创建组件
class Demo extends React.Component {
state = ({isHot: true})
showDate = () => {
const {input1} = this
alert(input1.value)
}
changeWeather = () => {
this.setState({
isHot: !this.state.isHot
})
}
render() {
return (
<div>
<h2>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
<input ref={(currentNode) => {
this.input1 = currentNode;
console.log('@@', currentNode)
}} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showDate}>点击我提示输入的数据</button>
<button onClick={this.changeWeather}>点我切换天气</button>
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
</body>
通过将 ref 的回调函数定义成 class 的绑定函数
的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
saveInput = (currentNode) => {
this.input1 = currentNode;
console.log('@@', currentNode)
}
render() {
return (
<div>
<input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/>
</div>
)
}
3.3.3、createRef创建ref容器
React.createRef()调用后可以返回一个容易,该容器可以存储被ref所标识的节点。此方式是最新的,官方所推荐的
<body>
//创建组件
class Demo extends React.Component {
myRef=React.createRef();
myRef2=React.createRef();
showDate = () => {
alert(this.myRef.current.value)
}
showDate2 = () => {
alert(this.myRef2.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showDate}>点击我提示左侧的数据</button>
<input ref={this.myRef2} onBlur={this.showDate2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))