React 之 组件实例的三大核心属性 (state props ref)
扩展:
在React中,构造函数仅用于以下两种情况
1)通过给this.state 赋值对象来初始化内部 state
2)为事件处理函数绑定实例
在构造函数中注意事项:
在为ReactComponent子类实现构造函数时,应该其他语句之前调用super(props).否则this.props在构造函数中会出现未定义的bug
说人话(翻译)即:传不传props,就看你要不要使用props
// constructor(props){ // super(props) // console.log(props) // props和this.props数据是一样的 // console.log(this.props) // } // constructor() // { // super() // console.log(this.props); //undefined // } constructor(props) { super() console.log(this.props) //undefined }
一.初识 state
1)state是组件对象醉重要的属性,值是对象(可以包含多个键值对数据)
2)组件被称为"状态机",通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意:
1)组件中render方法中的this为组件实例对象
2)组件自定义的方法中,this为undefined,如何解决?
a.强制绑定this,通过函数对象的bind()
b.箭头函数
3)状态数据,不能直接修改或更新
初始化state状态 案例 (绑定事件)
class Weather extends React.Component{ constructor(){ super() this.state = {isHot:true,wind:'大风'} } render(){ console.log(this) return <h1 onClick={demo}>今天天气很 {this.state.isHot?'炎热':'凉爽'}</h1> } } ReactDOM.render(<Weather/>,document.getElementById('test')) function demo(){
console.log(this) // undefined console.log('标题被点击了'); } 如何事件绑定,如上代码
扩展: 原生js有哪几种事件绑定方式
1)节点.addEventListener('click',()=>{ alert('点击了')})
2)节点.onclick = ()=>{}
3)<button onclick="demo()">
变更类中this的指向问题
// 创建组件 class Weather extends React.Component{ constructor(){ super() this.state = {isHot:true,wind:'大风'} //通过给state赋值对象来初始化内部state this.changeWeather = this.changeWeather.bind(this) //为事件处理函数绑定实例 } render(){ console.log(this); this.changeWeather() return <h1 onClick={this.changeWeather}>今天天气很 {this.state.isHot?'炎热':'凉爽'}</h1> } changeWeather(){ console.log(this); } } ReactDOM.render(<Weather/>,document.getElementById('test'))
如何修改 state的数据? 通过setState
class Weather extends React.Component{ constructor(props){ /* constructor 调用了几次 只有组件实例化时,才会执行一次,这里只有一次 */ console.log('constructor') super(props) this.state = {isHot:false} this.changeWeather = this.changeWeather.bind(this) } render(){ /* render 调用了几次? 执行了1+n次 实例化执行了一次,然后状态发生变化时就会执行 */ console.log('render') return <h1 onClick={this.changeWeather}>今天天气很 {this.state.isHot?'炎热':'凉爽'}</h1> } changeWeather(){ console.log('changeWeather') const {isHot} = this.state this.setState({isHot:!isHot}) // console.log(this); } } ReactDOM.render(<Weather/>,document.getElementById('test'))
简化写法
// 创建组件 class Weather extends React.Component{ // 初始化状态 state = {isHot:false} render(){ const {isHot} = this.state return <h1 onClick={this.changeWeather}>今天天气很 {isHot?'炎热':'凉爽'}</h1> } //自定义方法 changeWeather = ()=>{ const {isHot} = this.state this.setState({isHot:!isHot}) } } // 渲染组件到页面上 ReactDOM.render(<Weather/>,document.getElementById('test'))
二.初始props 是只读的
class Person extends React.Component { render() { return ( <ul> <li>姓名:{this.props.name}</li> <li>性别:男</li> <li>年龄:{this.props.age}</li> </ul> ) } } const p = {name:'张三',} ReactDOM.render(<Person name="tom" age="20"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="Jon" age="18"/>,document.getElementById('test1'))
优化写法
class Person extends React.Component { render() { let {name,age,gender} =this.props return ( <ul> <li>姓名:{this.props.name}</li> <li>性别:{this.props.gender}</li> <li>年龄:{this.props.age}</li> </ul> ) } } const p = {name:'张三',age:20,gender:'男'} // ReactDOM.render(<Person name="tom" age="20"/>,document.getElementById('test1')) ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))
对props进行限制
注意:需要引用 prop-types.js 因为在15.几的时候这样使用的,但是16.几之后,React.PropTypes.string.isRequired就被修改了
具体原因看下面代码块中的(tip)
class Person extends React.Component { render() { let {name,age,gender} =this.props return ( <ul> <li>姓名:{this.props.name}</li> <li>性别:{this.props.gender}</li> <li>年龄:{this.props.age}</li> </ul> ) } } Person.propTypes = { //propTypes 小写 react就会识别是要进行prop限制
//name:React.PropTypes.string.isRequired, // tips:版本15.几的时候把这种功能添加到了react.development.js上,变的笨重 name:PropTypes.string.isRequired, //PropTypes 大写 即:数据类型 gender:PropTypes.string } Person.defaultProps = { //默认值 gender:'不难不女', name:'李四' } const p = {name:'张三',age:20} // ReactDOM.render(<Person name="tom" age="20"/>,document.getElementById('test1')) ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))
props的简写方式
class Person extends React.Component { static propTypes = { name:PropTypes.string.isRequired, gender:PropTypes.string, age:PropTypes.number } static defaultProps = { gender:'男', age:18 } render(){ console.log(this) const {name,age,gender} = this.props return ( <ul> <li>名字:{name}</li> <li>年纪:{age}</li> <li>性别:{gender}</li> </ul> ) } } const p= {name:'李四',age:20,gender:'男'} ReactDOM.render(<Person name="张三" gender="女"/>,document.getElementById('test1')) ReactDOM.render(<Person {...p }/>,document.getElementById('test2'))
类组件中 构造器和props
注:看扩展内容
函数组件使用 props (函数组件只能玩转props)
三.初始ref 最推荐第三种 React.createRef()
第一种:String 类型的Refs ,在未来会被移除 简单案例(了解即可)
原因:string类型refs存在一些问题,太多了就会效率不高问题
class Demo extends React.Component { render(){ return ( <div> <input type="text" ref="input1" placeholder="点击按钮提示数据"/> <button onClick={this.handleClick}>点我提示左侧的数据</button> <input type="text" ref="input2" placeholder="失去焦点提示数据" onBlur={this.handleBlur}/> </div> ) } handleClick= () => { console.log(this,1111) console.log(this.refs.input1.value) } handleBlur=() => { const {input2} = this.refs console.log(input2.value) } } ReactDOM.render(<Demo/>,document.getElementById('test1'))
第二种: 回调函数形式的ref
内联的回调函数会在state发生变化时,执行两次,怎么解决,但是并不影响开发,也经常会用到这种
class Demo extends React.Component { state={ isHot:false } render() { return ( <div> <h2 onClick={this.handleHot}>今天的天气怎么?{this.state.isHot?'凉爽':'炎热'}</h2> /* 内联方式,这里的回调函数执行了几次 2次 是要state发生变化,先清空currentNode,然后在去执行第二次
通过内联形式,每次去执行时就相当于是一个新的函数,所以currentNode就是空的
*/ <input ref={currentNode => {this.input1 = currentNode;console.log('@',currentNode) } } type="text"/> <button onClick={this.handleClick}>点击</button> </div> ) } handleClick=()=>{ const {input1} = this console.log(input1.value) } handleHot=()=>{ const {isHot} = this.state this.setState({isHot:!isHot}) } } ReactDOM.render(<Demo/>,document.getElementById('test1'))
外联回调
class Demo extends React.Component { state={ isHot:false } callback = (currentNode) => { this.input1 = currentNode console.log(currentNode) } render() { return ( <div> <h2 onClick={this.handleHot}>今天的天气怎么?{this.state.isHot?'凉爽':'炎热'}</h2> /* 内联方式,这里的回调函数执行了几次 2次 是要state发生变化,先清空currentNode,然后在去执行第二次 之所以会清空currentNode是因为执行第一次是,去执行时,内联函数是一个新的函数 */ <input ref={ this.callback } type="text"/> <button onClick={this.handleClick}>点击</button> </div> ) } handleClick=()=>{ const {input1} = this console.log(input1.value) } handleHot=()=>{ const {isHot} = this.state this.setState({isHot:!isHot}) } } ReactDOM.render(<Demo/>,document.getElementById('test1'))
第三种:createRef的使用 (最推荐)
/* react.create调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是"专人专用"的 */ myRef = React.createRef() myRef2 = React.createRef() showData = ()=>{ console.log(this.myRef.current.value) console.log(this.myRef2.current.value) console.log('哈哈') } render(){ return ( <div> <input type="text" ref={this.myRef} placeholder="点击提交按钮"/> <button onClick={this.showData }>点我输出左边信息</button> <input type="text" ref={this.myRef2} placeholder="点击提交按钮"/> </div> ) } } ReactDOM.render(<Demo/>,document.getElementById('test1'))