4、使用state、setState处理数据
状态及数据,是组件内部得私有数据,只可以在数据内部使用,在状态改变时会自动调用render函数
state和setState:
state用于存储数据,setState是一个父类上得方法,用于修改state中的数据。通过setState修改数据可以使页面得数据一并发生改变(数据驱动视图),注意this指向
// 类组件中使用状态state class ClassHello extends React.Component { constructor() { super() // 初始化state数据 this.state = { num: 0 } } // state简化写法: // state = { // num: 0 // } render() { return ( <div> <p> 总计:{this.state.num} <span className='hello' onClick={() => { this.setState({ num: this.state.num + 1 }) }}> 点击+1! </span> </p> </div> ) } }
setState扩展:setState是同步还是异步?
在 React 中,setState方法是用于修改state状态值的,它本身是一个同步函数
如果是由 React 引发的事件处理 (比如通过 onClick 引发的事件处理),调用setState 不会同步更新 this.state ,除此之外的 setState 调用会同步更新 this.state
所谓的除此之外,指的是绕过 React,通过 addEventListener 直接添加的事件处理函数,还有通过 setTimeout/setInterval 产生的异步任务
原因:在 React 的函数实现,setState会根据一个变量 isBatchingUpdate 判断是直接更新this.state 还是放到队列中回头再说,而且 isBatchingUpdate 默认是 false,也就是表示setState 会同步更新 this.state,但是,有一个函数 batchedUpdate,这个函数会把isBatchingUpdate 修改为 true,而当 React 在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由 React 控制的事件处理过程 setState 不会同步更新this.state
要想让他同步直接执行,我们就需要脱离react的控制,使用setTimeout或promise、axios等
// 使用setState跟更新state的两种方式 (1). setState(stateChange, [callback])------对象式的setState 1.stateChange为状态改变对象(该对象可以体现出状态的更改) 2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用 (2). setState(updater, [callback])------函数式的setState 1.updater为返回stateChange对象的函数。 2.updater可以接收到state和props。 3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。 总结: 1.对象式的setState是函数式的setState的简写方式(语法糖) 2.使用原则: (1).如果新状态不依赖于原状态 ===> 使用对象方式 (2).如果新状态依赖于原状态 ===> 使用函数方式 (3).如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取
5、refs属性
5.1、通过字符串形式注册(不推荐)
class Demo extends React.Component{ //展示左侧输入框的数据 showData = ()=>{ //获取对应的文本框节点 const inputdom = this.refs.input1 alert(inputdom.value) } //展示右侧输入框的数据 showData2 = (e)=>{ /* 方法1:通过ref 获取对应的dom节点 */ // const {input2} = this.refs // alert(input2.value) /* 方法2:通过事件对象中的target属性获取对应的dom节点 event.target 代表的是当前触发事件的dom对象 */ console.log(e.target.value); } render(){ return( <div> <input ref="input1" type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧的数据</button> <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/> </div> ) } }
5.2、通过事件回调形式注册(推荐)
//创建组件 class Demo extends React.Component{ //展示左侧输入框的数据 showData = ()=>{ const dom=this.input1 alert(dom.value) } //展示右侧输入框的数据 showData2 = ()=>{ const {input2} = this alert(input2.value) } render(){ return( <div> <input ref={e=>this.input1=e} type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧的数据</button> {/* element参数就是这个dom元素 */} <input onBlur={this.showData2} ref={element => this.input2 = element } type="text" placeholder="失去焦点提示数据"/> </div> ) } }
5.3、通过react的API形式注册(createRef)
class Demo extends React.Component{ // 第一步、首先在组件实例上定义一个容器 myRef = React.createRef() //展示左侧输入框的数据 showData = ()=>{ const dom=this.input1 alert(dom.value) } //展示右侧输入框的数据 showData2 = ()=>{ const {myRef} = this alert(myRef.value) } render(){ return( <div> <input ref={e=>this.input1=e} type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧的数据</button> {/* 第二步、注册 */} <input onBlur={this.showData2} ref={this.myRef } type="text" placeholder="失去焦点提示数据"/> </div> ) } }
6、处理this指向
第一种解决方法(在点击事件中使用箭头函数包方法):
// 类组件中使用状态state class ClassHello extends React.Component { // state简化写法: state = { num: 0 } addnum() { // 据上节,如果把方法另拿出来,显得比较清晰,但是会出现this指向问题 this.setState({ num: this.state.num + 1 }) console.log('this',this); } render() { return ( <div> <p> 总计:{this.state.num} {/* 箭头函数不绑定this关键字,函数中this指向的是函数定义位置的上下文this。 简单理解:看箭头函数外层是否有函数, 如有,外层函数的this就是箭头函数的this。 如没有,this就是window */} <span className='hello' onClick={()=>{this.addnum()}}> 点击+1! </span> </p> </div> ) } }
第二种解决方法(把定义的方法改为箭头函数形式):
// 类组件中使用状态state class ClassHello extends React.Component { constructor() { super() // 初始化state数据 this.state = { num: 0 } } // 这是放在实例对象上面的方法 addnum = ()=>{ this.setState({ num: this.state.num + 1 }) console.log('this',this); } render() { return ( <div> <p> 总计:{this.state.num} {/* 箭头函数不绑定this关键字,函数中this指向的是函数定义位置的上下文this。 简单理解:看箭头函数外层是否有函数, 如有,外层函数的this就是箭头函数的this。 如没有,this就是window */} <span className='hello' onClick={this.addnum}> 点击+1! </span> </p> </div> ) } }
第三种解决方法(使用bind方法)
// 类组件中使用状态state class ClassHello extends React.Component { constructor() { super() // 初始化state数据 this.state = { num: 0 } // 在constructor里改变this指向 this.addnum=this.addnum.bind(this) } // 这是放在原型上面的方法 addnum() { this.setState({ num: this.state.num + 1 }) console.log('this',this); } render() { return ( <div> <p> 总计:{this.state.num} {/* 箭头函数不绑定this关键字,函数中this指向的是函数定义位置的上下文this。 简单理解:看箭头函数外层是否有函数, 如有,外层函数的this就是箭头函数的this。 如没有,this就是window */} <span className='hello' onClick={this.addnum}> 点击+1! </span> </p> </div> ) } }
高阶函数:
如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){ return(b)=>{ return (c)=>{ return a+b+c } } }
使用情景,当需要两个不确定参数时使用的函数
7、组件通信
当我们把组件拆开后,会涉及到一个数据之间传输的问题,那就是通信问题
7.1、父传子通信
父子通信通过props关键字来获取父组件传输的值
对props传参进行数据类型限制
<!-- 引入prop-types,用于对组件标签属性进行限制 --> <script type="text/javascript" src="../js/prop-types.js"></script> <!-- 类组件的props传参 --> <script type="text/babel"> class Person extends React.Component{ //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props constructor(props){ super(props) console.log('constructor',this.props); } render(){ // console.log(this); const {name,age,sex} = this.props //props是只读的 //this.props.name = 'jack' //此行代码会报错,因为props是只读的 return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age+1}</li> </ul> ) } } //对标签属性进行类型、必要性的限制 Person.propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 speak:PropTypes.func,//限制speak为函数 } //指定默认标签属性值 Person.defaultProps = { sex:'男',//sex默认值为男 age:18 //age默认值为18 } //渲染组件到页面 ReactDOM.render(<Person name={100} speak={speak}/>,document.getElementById('test1')) const p = {name:'老刘',age:18,sex:'女'} // console.log('@',...p); // 传参的简写方式,只能在react里使用 ReactDOM.render(<Person {...p}/>,document.getElementById('test3')) function speak(){ console.log('我说话了'); } </script> <!-- 函数组件使用props --> <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, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 } //指定默认标签属性值 Person.defaultProps = { sex:'男',//sex默认值为男 age:18 //age默认值为18 } </script>
类属性又被称为静态属性,所以我们可以在类里限制
class Person extends React.Component{ //对标签属性进行类型、必要性的限制 static propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 speak:PropTypes.func,//限制speak为函数 } //指定默认标签属性值 static defaultProps = { sex:'男',//sex默认值为男 age:18 //age默认值为18 } render(){ // console.log(this); const {name,age,sex} = this.props //props是只读的 //this.props.name = 'jack' //此行代码会报错,因为props是只读的 return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age+1}</li> </ul> ) } }