1
| <meta name="referrer" content="no-referrer" />
|
一、组件三大属性-state
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
- 组件被称为”状态机”, 通过更新组件的state来更新对应的页面显示(重新渲染组件)
- 注意组件中自定义方法中的this问题(事件调用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <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 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1> } changeWeather() { //changeWeacher放到哪里? -- Weather的原型对象上,供实例使用 //由于changeWeacher是作为onClick的回调,所以不是通过实例调用的,是直接调用的 //类中的方法默认开启了局部的严格模式,所以changeWeacher中的this为undefined console.log(this); //undefined } } //渲染组件到页面 ReactDOM.render(<Weather />, document.getElementById('test')) </script>
|
- 由于changeWeacher是作为onClick的回调,所以不是通过实例调用的,是直接调用的
- 类中的方法默认开启了局部的严格模式,所以changeWeacher中的this为undefined
- 如何修改this指向?
1 2 3 4 5
|
this.changeWeather = this.changeWeather.bind(this)
|
1 2 3 4 5 6 7 8 9 10 11
|
const isHot = this.state.isHot
this.setState({ isHot: !isHot })
|
- 简写方式:赋值语句形式+箭头函数
- 类中,要添加一个属性,可以采用直接赋值的方式
1 2 3 4 5 6 7 8 9 10 11
| <script> class Person { constructor(name, age) { this.name = name this.age = age } grade = '高一' } const p1 = new Person('jjj', 20) console.log(p1); //Person {grade: '高一', name: 'jjj', age: 20} </script>
|
- 所以constructor中的state可以采用直接赋值的方式
- 同理changeWeather也可以采用直接直接赋值的方式,把他放到weather实例上
- 但要使用箭头函数,因为普通函数this严格模式下为undefined、箭头函数this为上层作用域的this也就是weath实例
- 所以简写后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <script type="text/babel"> //1.创建组件 class Weather extends React.Component { //简化不使用constructor //我们为了修改state的初始状态才被动的去使用constructor //类中 我们可以使用直接赋值的方式去添加属性 如 a = 1 //constructor可以省略了 // constructor(props) { // super(props) // } //所以这里可以直接state state = { isHot: true, wind: '微风' } render() { const { isHot, wind } = this.state return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1> } //这里也可以采用直接赋值的方式把方法放到实例本身 //注意使用箭头函数 this查找到上层作用域也就是weather实例 //普通函数this为undefined changeWeather = () => { const isHot = this.state.isHot this.setState({ isHot: !isHot }) } } //渲染组件到页面 ReactDOM.render(<Weather />, document.getElementById('test')) </script>
|
二、组件三大属性-props
理解
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
作用
- 通过标签属性从组件外向组件内传递变化的数据
- 注意:组件内部不要修改props数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { render() { const {name,age,sex} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } } //渲染组件到页面 ReactDOM.render(<Person name="xy" age = "18" sex = "男"/>, document.getElementById('test')) </script>
|

扩展运算符的复习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <body> <script> let arr1 = [1, 2, 3, 4] let arr2 = [5, 6, 7, 8]
console.log(...arr1);
console.log([...arr1, ...arr2]);
function sum(...nums) { return nums.reduce((preValue, currentValue) => { return preValue + currentValue }) } console.log(sum(1, 2, 3, 4)); console.log(sum(1, 2));
let person = { name: 'Tom', age: 20 } let person2 = { ...person } person.name = 'Jack' console.log(person2);
let person3 = { ...person, name: 'Rose', address: "China" } console.log(person3); </script> </body>
|
批量传递props
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { render() { const { name, age, sex } = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } } const p = { name: 'Jack', age: 20, sex: '男' } //渲染组件到页面 ReactDOM.render(<Person {...p} />, document.getElementById('test')) </script>
|
注意{…p} 与js中的{}含义不一样,js中是构造对象的固定写法,react中的{}是放在标签中的写法
而且这种写法只能用于标签写法中ReactDOM.render(<Person {…p} />, document.getElementById(‘test’))
对props进行限制
如果想把页面上的age都+1,但是原来数据不改变,可以这样
但是如果我们传递的age如果是字符串属性,那么就是字符串拼接而不是相加了
也可以这样写
1
| ReactDOM.render(<Person name="Tom" age={20} sex="女"/>, document.getElementById('test'))
|
这样可以正常的age+1,但是别人在复用我们写的组件时,不知道age+1的需求呢?他还是写成age=”20”这种形式呢?
另外,如果我们不对sex进行赋值,那页面就会不会显示,交互不是很好,所以我们需要写一个默认值,同理age也是如此
name属性是必须的,不能不进行赋值
所以我们需要用官方的方法对props进行限制
在react15.5版本及以下我们可以这样写:
1 2 3
| Person.propTypes = { name:React.PropTypes.属性 }
|
但是在react15.5版本以后,移除了这种方法,取而代之的是要引入prop-types.js文件
写法也发生改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!-- 引入prop-types文件,对props进行限制 --> <script src="../js/prop-types.js"></script>
//对标签属性进行类型,必要性的限制 Person.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number, sex: PropTypes.string, speak:PropTypes.func //限制speak为函数,注意写法 }
//指定默认标签属性值 Person.defaultProps = { sex: '不男不女', age: 18 }
function speak() { console.log('说话'); }
const p = { name: 'jack', sex: '男', age: 19 } //渲染组件到页面 ReactDOM.render(<Person {...p} />, document.getElementById('test1')) ReactDOM.render(<Person name="Tom" age={20} sex="女" speak={speak}/>, document.getElementById('test2'))
|
props简写
类中static修饰符的使用
如果直接采用赋值,是给类的实例添加属性
如果使用static进行修饰,就是给类添加属性
类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { render() { const { name, age, sex } = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } //如果再类里直接给里添加属性,不是给实例,那么需要用static修饰 static propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number, sex: PropTypes.string, speak: PropTypes.func //限制speak为函数,注意写法func } static defaultProps = { sex: '不男不女', age: 18 } } function speak() { console.log('说话'); } const p = { name: 'jack', sex: '男', age: 19 } //渲染组件到页面 ReactDOM.render(<Person {...p} />, document.getElementById('test1')) ReactDOM.render(<Person name="Tom" age={20} sex="女" speak={speak} />, document.getElementById('test2')) </script>
|
类中的构造器与props
类中的构造函数一定要写吗?根据官方文档,使用构造器可以做两件事
1.初始化state
1
| this.state = { isHot: true, wind: '微风' }
|
2.为事件监听函数绑定实例
1
| this.changeWeather = this.changeWeather.bind(this)
|
但是这两种我们都有简写方式而不用使用constructor
所以constructor可写可不写
但是如果使用constructor,一定要进行props的传递与接收
1 2 3 4
| constructor(props) { super(props) console(this.props) }
|
如果不接受,会造成构造器中实例this为undefined(参考官方文档)
一句话概括:构造器使用的话,是否接收传递props,取决于:是否希望在构造器中通过this访问props
函数式组件使用props
因为函数可以传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script type="text/babel"> //创建函数式组件 function Person(props) { const { name, sex, age } = props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } //对标签属性进行类型,必要性的限制 Person.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number, sex: PropTypes.string, speak: PropTypes.func //限制speak为函数,注意写法 } //指定默认标签属性值 Person.defaultProps = { sex: '不男不女', age: 18 } //渲染组件到页面 ReactDOM.render(<Person name="Tom" age={20} />, document.getElementById('test2')) </script>
|
三、组件三大属性-refs
理解
组件内的标签可以定义ref属性来标识自己
应用(字符串形式的ref)
自定义组件,功能:
点击按钮,提示第一个输入框的值
当第二个输入框失去焦点时,提示输入框的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { showData1=() => { console.log(this.refs) //{input1: input, input2: input} 自定义value值:标签节点 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.showData1}>点击提示左侧数据</button> <input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点提示数据" /> </div> ) } } //渲染组件到页面 ReactDOM.render(<Person />, document.getElementById('test')) </script>
|
注意:这是字符串形式的ref,已经不再推荐使用(效率不高)。
回调形式的ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { showData1 = () => { console.log(this.refs); //{input1: input, input2: input} const { input1 } = this alert(input1.value) } showData2 = () => { const { input2 } = this alert(input2.value) } render() { return ( <div> <input ref={(currentNode) => { this.input1 = currentNode }} type="text" placeholder="点击按钮提示数据" /> <button onClick={this.showData1}>点击提示左侧数据</button> <input ref={(currentNode) => { this.input2 = currentNode }} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" /> </div> ) } } //渲染组件到页面 ReactDOM.render(<Person />, document.getElementById('test'))
</script>
|
回调函数的参数是ref所在的标签节点,只有ref标记的react才会去调用
1 2 3 4
| //利用箭头函数特性进行简写 <input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据" /> //这样的不会调用 <input hahahah={() => { console.log(1); }} type="text" placeholder="点击按钮提示数据" />
|

回调形式ref的执行次数问题
内联的回调,渲染时调用一次,但是更新时会调用两次,看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { state = { isHot: true } changeWeather = () => { const { isHot } = this.state this.setState({ isHot: !isHot })
} showData = () => { const { input1 } = this alert(input1.value) } render() { return ( <div> <h2>天气{this.state.isHot ? '炎热' : '凉爽'}</h2> <input ref={c => { this.input1 = c, console.log(c) }} type="text" placeholder="点击按钮提示数据" /> <button onClick={this.showData}>点击提示数据</button> <button onClick={this.changeWeather}>更新来看回调ref调用几次</button> </div> ) } } //渲染组件到页面 ReactDOM.render(<Person />, document.getElementById('test')) </script>
|
当我没进行更新时,只会调用一次。

此时我点击按钮进行更新页面:

可以看到,更新时调用了两次。

如何解决?进行类绑定的回调
1 2 3 4 5 6
| saveInput = (c) => { this.input1 = c console.log(c); } {/*进行类绑定类解决 注意:这是jsx语法注释写法{/**/}*/} <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据" />
|
不过影响不大,日常开发基本都用内联,方便一点
React.createRef()
1 2 3 4 5
| // React.createRef调用后可以返回一个容器 // 该容器可以存储被ref所标识的节点,该容器是“专人专用”的 myRef = React.createRef() <input ref={this.myRef}/>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script type="text/babel"> //创建类式组件 class Person extends React.Component { myRef = React.createRef() showData = () => { console.log(this.myRef); } render() { return ( <div> <input ref={this.myRef} type="text" placeholder="点击按钮提示数据" /> <button onClick={this.showData}>点击提示数据</button> </div> ) } } //渲染组件到页面 ReactDOM.render(<Person />, document.getElementById('test')) </script>
|

如何取值?
1 2 3
| showData = () => { console.log(this.myRef.current.value); }
|
注意:
- current的写法是固定的不能改
- React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
- 如果想要多次使用,需要多次创建
- 如果多次使用只创建一个,后面的会覆盖之前的
1 2 3 4
| myRef1 = React.createRef() myRef2 = React.createRef() <input ref={this.myRef2} type="text" placeholder="点击按钮提示数据" /> <input ref={this.myRef2} type="text" placeholder="点击按钮提示数据" />
|