前言
组件的三大核心属性
内容
state
定义一个展示天气信息组件,通过点击切换天气信息
理解
- state是组件对象最重要的属性,值是对象(可以包含多key-value组合)
- 组件被称为
状态机
,通过更新组件的state来重新渲染组件
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"> </div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ //1. 创建组件 class Weather extends React.Component { //调用1次 constructor(props) { super(props); //初始化状态 this.state = { isHot: true }; //解决changeWeather的指向问题 this.changeWeather = this.changeWeather.bind(this) } //调用次数1+n 1是初始化,n是状态更新 render() { console.log(this) //读取状态 const {isHot} = this.state return ( <div> <h1 onClick={this.changeWeather}>今天天气 {isHot ? '热热' : '不热热'}</h1> </div> ); } //点击几次调用几次 changeWeather(){ //状态不可直接更改需要借助内置方法(setState)进行更改 const isHot = this.state.isHot this.setState({isHot:!isHot}) } } //2.渲染组件到页面 ReactDOM.render(<Weather/>,document.getElementById('test')) </script> </body> </html>
简写
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"> </div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ //1. 创建组件 class Weather extends React.Component { render() { const {isHot} = this.state return ( <div> <h1 onClick={this.changeWeather}>今天天气 {isHot ? '热热' : '不热热'}</h1> </div> ); } //赋值语句+箭头函数 changeWeather = () => { const isHot = this.state.isHot this.setState({isHot:!isHot}) } } //2.渲染组件到页面 ReactDOM.render(<Weather/>,document.getElementById('test')) </script> </body> </html>
注意点
1. 组件中的render方法中的this为组件实例对象 2. 组件中自定义的方法中的为undefined,如何解决? 2.1 前置绑定this:通过函数对象的bind() 2.2 赋值语句+箭头函数 3. 状态数据,不能直接修改或更新,需通过setState来变更
props
自定义用来显示一个人员信息的组件
- 姓名必须指定,且为字符串类型;
- 性别为字符串类型,如果性别没有指定,默认为男
- 年龄为字符串类型,且为数字类型,默认值为18
理解
- 每个组件对象都会有props(properties)属性
- 组件标签的所有属性都保存在props中
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <div id="test1"></div> <div id="test2"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <!--引入prop-types,用于对组件标签属性进行限制--> <script type="text/javascript" src="../js/prop-types.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ //1. 创建组件 class Person extends React.Component { render() { //props是只读的,不可进行修改 const {name,age,sex} = this.props return ( <div> <ul> <li>姓名:{name}</li> <li>年龄:{age}</li> <li>性别:{sex}</li> </ul> </div> ); } } //对标签属性进行类型,必要性限制 Person.protoType = { name: PropTypes.string.isRequired, // 限制name必传且类型为string age: PropTypes.number, sex: PropTypes.string, speak: PropTypes.func } //指定默认标签属性值 Person.defaultProps = { sex: "男", age: 18 } function speak() { return "哈哈哈哈" } //2.渲染组件到页面 const p = {name:"张三", age:66, sex:'男'} ReactDOM.render(<Person name="tom" age={19} sex="男" speak={speak}/>,document.getElementById('test')) ReactDOM.render(<Person name="jim" age="10" sex="男"/>,document.getElementById('test1')) //语法糖 ReactDOM.render(<Person {...p}/>,document.getElementById('test2')) </script> </body> </html>
简写
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <div id="test1"></div> <div id="test2"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <!--引入prop-types,用于对组件标签属性进行限制--> <script type="text/javascript" src="../js/prop-types.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ //1. 创建组件 class Person extends React.Component { // constructor(props) { // //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props // super(props); // } //对标签属性进行类型,必要性限制 static protoType = { name: PropTypes.string.isRequired, // 限制name必传且类型为string age: PropTypes.number, sex: PropTypes.string } //指定默认标签属性值 static defaultProps = { sex: "男", age: 18 } render() { //props是只读的,不可进行修改 const {name,age,sex} = this.props return ( <div> <ul> <li>姓名:{name}</li> <li>年龄:{age}</li> <li>性别:{sex}</li> </ul> </div> ); } } ReactDOM.render(<Person name="tom" />,document.getElementById('test')) </script> </body> </html>
函数式组件使用props
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <div id="test1"></div> <div id="test2"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <!--引入prop-types,用于对组件标签属性进行限制--> <script type="text/javascript" src="../js/prop-types.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ //1. 创建组件 function Person(props) { const {name, age, sex} = props return ( <div> <ul> <li>姓名:{name}</li> <li>年龄:{age}</li> <li>性别:{sex}</li> </ul> </div> ) ; } //对标签属性进行类型,必要性限制 Person.protoType = { name: PropTypes.string.isRequired, // 限制name必传且类型为string age: PropTypes.number, sex: PropTypes.string } //指定默认标签属性值 Person.defaultProps = { sex: "男", age: 18 } ReactDOM.render(<Person name="tom" sex="女" age={18}/>,document.getElementById('test')) </script> </body> </html>
注意点
1. 通过标签属性从组件外向组件内传递变化的数据 2. 注意: 组件内部不要修改props数据 3. 内部通过this.props.xx读取某个属性值 4. props中的属性值进行类型限制和必要性限制 4.1 React v15.5 开始已弃用 Person.propTypes = { name: React.PropTypes.string.isRequired, age: React.PropTypes.number } 4.2 使用prop-types库进限制(需要引入prop-types库) Person.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number. } 5. 扩展属性: 将对象的所有属性通过props传递 <Person {...person}/> 6. 默认属性值 Person.defaultProps = { age: 18, sex:'男' }
ref
- 点击按钮, 提示第一个输入框中的值
- 当第2个输入框失去焦点时, 提示这个输入框中的值
理解
组件内的标签可以定义ref属性来标识自己
字符串类型ref
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以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/> <hr/> <input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点提示数据"/> </div> ); } } ReactDOM.render(<Demo/>, document.getElementById("test")) /** * string类型的ref是过时的API * https://react.docschina.org/docs/refs-and-the-dom.html#legacy-api-string-refs * * https://github.com/facebook/react/pull/8333#issuecomment-271648615 */ </script> </body> </html>
回调函数类型ref
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以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={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧数据</button> <br/> <hr/> <input ref={currentNode => this.input2 = currentNode} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/> </div> ); } } ReactDOM.render(<Demo />, document.getElementById("test")) </script> </body> </html>
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ class Demo extends React.Component { state = {isHot:true} changeWeather = () => { const {isHot} = this.state this.setState({isHot:!isHot}) } //左侧 showData = () => { const {input1} = this alert(input1.value) } saveInput = (c) => { this.input1 = c; console.log('@',c); } render() { const {isHot} = this.state return ( <div> <h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2> <button onClick={this.changeWeather}>点击切换天气</button> {/*<input ref={(currentNode) => { this.input1 = currentNode; console.log('@',currentNode);} } type="text" placeholder="点击按钮提示数据"/> */} <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧数据</button> </div> ); } } ReactDOM.render(<Demo />, document.getElementById("test")) </script> </body> </html>
createRef
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以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> <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="=失去焦点显示数据"/> </div> ); } } ReactDOM.render(<Demo />, document.getElementById("test")) </script> </body> </html>
注意点
1. string类型的ref是过时的API,可能会在未来版本被移除,建议使用回调函数或createRef API来代替。 <input ref="input1"/> 2. 回调函数类型的ref <input ref={(c)=>{this.input1 = c}} /> 2.1 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素 <input ref={this.saveInput} /> //通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题 3. createRef创建ref容器 myRef = React.createRef() <input ref={this.myRef}/>
事件处理
1.通过onXxx属性指定事件处理函数(注意大小写) 1) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件___兼容性 2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)___高效性 2.通过event.target得到发生事件的DOM元素对象___不要过度使用ref
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--创建"容器"--> <div id="test"></div> <!--引入react核心库--> <script type="text/javascript" src="../js/react.development.js"></script> <!--引入react-dom,用于支持react操作DOM--> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!--引入babel,用于将jsx转js--> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel">/*一定要以text/babel来声明*/ class Demo extends React.Component { /** 1. 通过onXxx属性指定事件处理函数(注意大小写) 1) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件___兼容性 2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)___高效性 2. 通过event.target得到发生事件的DOM元素对象___不要过度使用ref * */ // 创建ref容器 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> <input onBlur={this.showData2} type="text" placeholder="=失去焦点显示数据"/> </div> ); } } ReactDOM.render(<Demo />, document.getElementById("test")) </script> </body> </html>
学无止境,谦卑而行.