一、三大属性之一:state
默认状态下React.Components会给我们定义构造器(类似于无参构造函数一样),但是默认是把state设置为null的,那么如果我们要自定义初始化的state的话,那么我们就要像(有参构造函数一样)自定义构造函数了
如何自定义构造函数呢?
先上代码:
class Weather extends React.Component { constructor(props) { super(props) this.state = {isHot: false} this.changeWeacher = this.changeWeacher.bind(this) } render() { return <h1 onClick={this.changeWeacher}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1> } // 不写 function changeWeacher() { console.log(this) } }
其中的构造函数模块:
constructor(props) { super(props) this.state = {isHot: false} this.changeWeacher = this.changeWeacher.bind(this) }
一个重要的细节:
自定义构造函数,必须要执行
super(props)
为什么?
并且 为什么super()要放在构造函数最上面执行呢?
ES6语法中,super指代的其实是父类的构造函数,也就是React.Component的构造函数了,
在你调用super( ) 之前,你无法在构造函数中使用this
执行 super(props) 可以使基类 React.Component 初始化 this.props。
另外一个疑问:有时候我们不传 props,只执行 super(),或者没有设置 constructor 的情况下,依然可以在组件内使用 this.props,为什么呢?
其实 React 在组件实例化的时候,马上又给实例设置了一遍 props:
// React 内部 const instance = new YourComponent(props); instance.props = props;虽然 React 会在组件实例化的时设置一遍 props,但在 super 调用一直到构造函数结束之前,this.props 依然是未定义的(直接报错了)
class Button extends React.Component { constructor(props) { super(); // ? 我们忘了传入 props console.log(props); // ✅ {} console.log(this.props); // ? undefined } // ... }
然后因为state要是对象的模式,所以通过以下语句进行初始化
this.state = {isHot: false}
拓展:类中的方法!
render() { return <h1 onClick={this.changeWeacher()}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1> }
如果是通过 this.changeWeacher() 的话 报错
因为在{ } 内是js语句,所以this.changeWeacher()是直接执行了函数,那么onclick得到的就是这个函数执行的结果了,所以这种方式的话,那么这个函数是一个柯里化函数
柯里化函数:该函数接收一个函数作为参数(比如数组的map方法),或者是该函数返回值是一个函数
如果我们定义函数的时候直接通过:
changeWeacher() { console.log(this) }那么这个时候this是undefined的,因为在babal的模式下,也就是严格模式下,changeWeacher函数里面的this是直接执行全局的,但是严格模式下不允许这个发生,所以执行的就是undefined
代码中通过如下方式(通过bind显示硬绑定this,让this执行的是该class类的实例了)
this.changeWeacher = this.changeWeacher.bind(this)
在函数中访问和修改state
当该函数的this不管是通过 箭头函数 还是通过显示绑定的方式让this执行了该实例的话,就可以通过this来访问state了
changeWeacher() { const isHot = this.state.isHot this.setState({isHot: !isHot}) }
并且只能通过 this.setState的方式改变state,直接赋值操作改变的话,改变不了状态的(赋值操作可以改变页面数值,但是内部的状态是没有改变的)
state的简写方式
class Weather extends React.Component { state = {isHot: false} render() { return <h1 onClick={this.changeWeacher}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1> } changeWeacher = () => { const isHot = this.state.isHot this.setState({isHot: !isHot}) } }
-
不适用构造函数初始化state,直接像定义private属性一样,直接定义类的属性
-
通过箭头函数的方式定义函数(因为即使在babel严格模式下,箭头函数不会收到严格模式的约束,在箭头函数中使用this访问属性的话,当前作用域找不到的话,就在外面作用域找
-
所以就可以在箭头函数的this中直接访问实例中定义的state了
-
二、三大属性之二:props
props的基本使用
<script type="text/babel"> class Person extends React.Component{ render() { return ( <ul> <li>年级1:{this.props.name}</li> <li>年级2:{this.props.sex}</li> <li>年级3:{this.props.age}</li> </ul> ) } } // ReactDOM.render(<Person age="19" />, document.getElementById('test')) // ReactDOM.render(<Person age={19}/>, document.getElementById('test')) const p = {name : 'gogocj', age: '19', sex: '男'} ReactDOM.render(<Person {...p} />, document.getElementById('test')) </script>
-
在类中中直接通过this.props访问传递过来的参数
-
如果是在jsx中的话,就要在 { } 中来访问this.props
-
-
传递参数相关
-
如果想要传输数字的话,要通过 <Person age={19}/>,也就是通过一个{ } 因为在{ }中式js语句,所以19就是js语句中的数字了
-
通过{ ...p } 三点运算展开符的方式来传递props
-
限制props的类型和默认值
class Person extends React.Component{ ........ } Person.propTypes = { name: PropTypes.string.isRequired, speak: PropTypes.func } Person.defaultProps = { sex: '未知' }
使用继承了React.Component的类自带的 propTypes属性
-
要求是串联的:
PropTypes.string.isRequired // 表示类型是字符串,并且是必须传的
-
默认值通过 自带的defaultProps属性
简写方式
上面都是直接通过Person.propTypes的方式来对规定类型限制的:
但是Person就是当前的类,所以完全可以省略Person,直接在这个类的内部定义一个static对象就可以了
class Person extends React.Component{ ............ static propTypes = { name: PropTypes.string.isRequired, speak: PropTypes.func } static defaultProps = { sex: '未知' } }
在构造器中使用props
constructor(props) { super(props) // 使用props }
-
如果要在构造器中使用的话就必须写 super(props) ,不然在构造器中使用props的话,就会直接的报错了
-
也就是说,构造器中不适用props就可以不定义,使用的话就要super一下
三、三大属性之三:refs
前言:
React的诞生很多都是为了减少对document的使用,而我们如果在js中要获取到对应元素的话,传统的方法都是直接使用document的getlementbyid,byClass等等,到那时在React为了减少document操作,使用的是refs
字符串类型的refs
-
class Demo extends React.Component{ showData = () => { const {input01} = this.refs alert(input01.value) } render() { return ( <div> <input id="input1" ref="input01" type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧的数据</button> </div> ) } }
我们通过在jsx中,用ref来表示jsx中程序员写的虚拟DOM,但是我们通过this.refs获取到的元素并不是虚拟DOM,而是虚拟DOM转化成真实DOM之后的节点
-
通过this.refs.input01就可以拿到这个input元素了
拓展:React对原生html的相关疯转
在原生里面使用 onclick、onblur等等,但是在React中使用的是onClick、onBlur等等
为什么呢?
React其实是在原生的onclick等等的基础上,进行了相关兼容性的封装,然后改了一下名字,也就是第二个单词首字母大写了,也就是onClick
回到函数形式的refs
前言:
字符串形式的ref可能在未来就废除了,因为过多的给原生定义refs来获取该元素信息,方便但是性能太低了
class Demo extends React.Component{ showData = () => { const {input01} = this alert(input01.value) } render() { return ( <div> <input ref={ c => this.input01 = c} type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧的数据</button> </div> ) } }
-
直接在ref中调用箭头函数,参数就是该元素(代码中用c表示),直接通过this.input01 = c吧这个元素挂载到this上了,因为是箭头函数,所以此时的this执行的是该类实例。
使用内联函数的ref
class Demo extends React.Component{ saveInput = (c) => { this.input1 = c console.log('@',c) } render() { return ( <div> <input ref={ this.saveInput } type="text"/> <button onClick={this.showInfo}>点我</button> </div> ) } }
但是这种内联函数是有一个问题的
-
saveInput中:就是在状态更新的时候会执行两次,第一次c拿到的是null,第二次拿到的才是该元素,更新指的是状态的更新,也就是再一次调用render函数,但是点击和刷新就不会出现这个问题了 ,这是因为在每次渲染render的时候都会创建一个新的函数实例,所以React清空旧的并且设置新的。
另一种方式:React.createRef
class Demo extends React.Component{ myRef1 = React.createRef() myRef2 = React.createRef() showData1 = () => { alert(this.myRef1.current.value) } showData2 = () => { alert(this.myRef2.current.value) } render() { return ( <div> <input ref={this.myRef1} type="text" placeholder="点击按钮提示数据1"/> <button onClick={this.showData1}>点我提示左侧的数据</button> <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/> </div> ) } }
-
ref={this.myRef} 调用之后,自动把该标签放到这个容器里面
-
但是该容器是“专人专用”的,如果多个标签同时使用的话,那么后放进去的标签就会把前面的顶掉了
-
虽然这个是要用多少个就要挂载多少个在实例上,但是这种方式是目前react最推荐的一种方式了