1.state
state是组件中最重要的一个属性,值是对象(可包含多个key-value组合)
组件被称为"状态机",通过更新组件的state来更新对应的页面显示(重新渲染组件)
注意:
①组件中render方法中的this为组件实例对象
②组件自定义的方法中的this位undefined,如何解决?
通过函数对象的bind函数强制绑定this 或者 使用箭头函数
③状态数据,需要通过setState()函数来修改,不能直接更改
小案例
实现点击切换的效果
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"></meta>
<title></title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot:false}
// 类中的方法默认开启了局部的strict模式,所以changeWeather中的this位undefined
// 解决changeWeather中this指向的问题
this.cWeather = this.changeWeather.bind(this)
}
render() {
const {isHot} = this.state
return <h2 onClick={this.cWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
}
changeWeather() {
// 状态state不可直接修改,需要通过setState方法
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
</body>
</html>
简化写法(推荐)
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
}
state = {isHot:false}
render() {
const {isHot} = this.state
return <h2 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
}
// 自定义方法 --- 要用赋值语句的形式 + 箭头函数(用changeWeather = function(){}不行)
// 箭头函数下面的this,如果发现自己是undefined,就会向外层寻找,所以这里的this指向的是weather实例对象
changeWeather = () => {
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
补充:原生js中绑定点击事件的三种方式
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick="demo()">按钮3</button>
<script type="text/javascript">
// 第一种
const btn1 = document.getElementById('btn1')
btn1.addEventListener('click', ()=>{
alert('按钮1被点击了')
})
// 第二种
const btn2 = document.getElementById('btn2')
btn2.onclick = ()=>{
alert('按钮2被点击了')
}
// 第三种
function demo() {
alert('按钮3被点击了')
}
</script>
2.props
React组件中的props主要是用来接收传入的数据的
小案例
需求:自定义一个用来显示个人信息的组件
姓名必须指定,并且为字符串类型
性别为字符串类型,没有指定就默认为男
年龄为数字类型,默认值为18
prop-types.js下载地址:https://unpkg.com/prop-types@15.6.2/prop-types.js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"></meta>
<title>Document</title>
</head>
<body>
<div id="test1"></div>
<div id="test2"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,全局就多了一个propTypes对象,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component {
render() {
const {name,age,sex,speak} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
}
// 对标签属性进行类型、必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
// 指定默认标签属性值
Person.defaultProps = {
sex: '男',
age: 18
}
const p1 = {name: 'Tom', age:18, sex: '男', speak: speak}
const p2 = {name: 'Nancy', age:19, sex: '女'}
function speak() {
console.log('我说话了')
}
ReactDOM.render(<Person {...p1}/>, document.getElementById('test1'))
ReactDOM.render(<Person {...p2}/>, document.getElementById('test2'))
</script>
</body>
</html>
也可以把标签属性的限制放在类里面(推荐)
class Person extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
static defaultProps = {
sex: '男',
age: 18
}
render() {
const {name,age,sex,speak} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
}
上面的都是用类式组件写的,下面我们用函数式组件写一下
// 其它代码同上
<script type="text/babel">
function Person (props) {
const {name,age,sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func
}
Person.defaultProps = {
sex: '男',
age: 18
}
const p1 = {name: 'Tom', age:18, sex: '男'}
const p2 = {name: 'Nancy', age:19, sex: '女'}
ReactDOM.render(<Person {...p1}/>, document.getElementById('test1'))
ReactDOM.render(<Person {...p2}/>, document.getElementById('test2'))
</script>
3.refs
组件中的标签可以用refs来标识自己,类似于id,但用法不同
小案例
点击按钮,显示输入框的数据
失去焦点,显示输入框的数据
①String 类型的 Refs (效率低,官方不推荐使用了)
// 其它代码同上
<script type="text/babel">
class Demo extends React.Component {
showData = () => {
const {input1} = this.refs
alert(input1.value)
}
showData2 = () => {
const {input2} = this.refs
alert(input2.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button><br/>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
②回调函数形式的 Refs
// 其它代码同上
<script type="text/babel">
class Demo extends React.Component {
showData = () => {
const {input1} = this
alert(input1.value)
}
showData2 = () => {
const {input2} = this
alert(input2.value)
}
render() {
return (
<div>
<input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button><br/>
<input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
上面的 ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。想要避免,用类绑定的形式,看下面。
// 其它代码同上
<script type="text/babel">
class Demo extends React.Component {
showData = () => {
const {input1} = this
alert(input1.value)
}
showData2 = () => {
const {input2} = this
alert(input2.value)
}
assignment1 = c => { this.input1 = c;}
assignment2 = c => { this.input2 = c;}
render() {
return (
<div>
<input ref={this.assignment1} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button><br/>
<input ref={this.assignment2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
③ 用React.createRef() 返回的容器存储refs标识的结点(推荐)
// 其它代码同上
<script type="text/babel">
class Demo extends React.Component {
// React.createRef()调用后返回一个容器,该容器可以存储被ref标识的结点,一个容器装一个结点
myRef = React.createRef()
myRef2 = React.createRef()
showData = () => {
alert(this.myRef.current.value)
}
showData2 = () => {
alert(this.myRef2.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button><br/>
<input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
4.事件处理
通过onXxx属性指定时间处理函数(注意大小写)
React使用的是自定义(合成)事件,而不是使用原生DOM事件
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
通过event.target得到发生事件的DOM元素对象
我们把上面的例子改一下,官方文档告诉我们不要过多使用refs,当发生事件的元素和事件操作的元素是同一个时,我们可以省略标签的ref,用event.target获取该元素
// 其它代码同上
<script type="text/babel">
class Demo extends React.Component {
myRef = React.createRef()
myRef2 = React.createRef()
showData = () => {
alert(this.myRef.current.value)
}
showData2 = (event) => {
alert(event.target.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button><br/>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>