组件三大核心属性: 1.state state的主要作用是用于组件保存 、控制、修改自己的状态。在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。这里你可以认为state是一个局部的、只能被自身控制的数据源。state中状态可以通过this.setState方法进行更新,这会导致组件的重新渲染。
没有state的组件叫无状态组件,设置了state的叫有状态组件,因为状态会带来管理的复杂性,我们应该尽可能的多写无状态的组件,降低代码的维护程度,也会在一定程度上增强组件的复用性。
组件中 render方法中的this为组件实例对象
组件自定义方法中this为undefined,如何解决?
强制绑定 this;通过函数对象的 bind()
箭头函数
状态数据,不能直接修改更新
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <div id ="test" > </div > <script type ="text/javascript" src ="../js/react.development.js" > </script > <script type ="text/javascript" src ="../js/react-dom.development.js" > </script > <script type ="text/javascript" src ="../js/babel.min.js" > </script > <script type ="text/babel" > class Weather extends React.Component { state = { isHot :true , wind :'大风' } changeWeather = () => { let isHot = this .state .isHot this .setState ({isHot :!isHot}) } render ( ){ const {isHot,wind} = this .state return <h1 id ="title" onClick ={this.changeWeather} > 今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1 > } } ReactDOM .render (<Weather /> ,document .getElementById ('test' )) </script > </body > </html >
2.props props的主要作用是让使用该组件的父组件可以传入参数来配置该组件,它是外部传进来的配置参数,组件内部无法控制也无法修改,除非 外部组件主动传入新的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 let arr1 = [1 ,3 ,5 ,7 ,9 ]let arr2 = [2 ,4 ,6 ,8 ,10 ]console .log (...arr1);let arr3 = [...arr1,...arr2]function sum (...numbers ) { return numbers.reduce ((preNum,currentNum ) => { return preNum + currentNum }) } console .log (sum (1 , 2 , 3 , 4 ));let person = {name :'jerry' ,age :19 }let person2 = {...person}console .log (...person);let person3 = {...person,name :'jack' ,address :'地球' }
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <div id ="test" > </div > <div id ="test1" > </div > <div id ="test2" > </div > <script type ="text/javascript" src ="../js/react.development.js" > </script > <script type ="text/javascript" src ="../js/react-dom.development.js" > </script > <script type ="text/javascript" src ="../js/babel.min.js" > </script > <script type ="text/javascript" src ="../js/prop-types.js" > </script > <script type ="text/babel" > class Person extends React.Component { render ( ){ console .log (this ) const {name,age,sex} = this .props return ( <ul > <li > 姓名:{name}</li > <li > 性别:{sex}</li > <li > 年龄:{age}</li > </ul > ) } } Person .propTypes = { name :PropTypes .string .isRequired , sex :PropTypes .string , age :PropTypes .number , speak :PropTypes .func } Person .defaultPrps = { sex :'middle' , age :19 } ReactDOM .render (<Person name ="jerry" speak ={speak} age ={18} sex ="男" /> ,document .getElementById ('test' )) ReactDOM .render (<Person name ="tom" age ={18} sex ="男" /> ,document .getElementById ('test1' )) const p = {name :'sun' ,age :18 ,'sex' :'female' } ReactDOM .render (<Person {...p }/> ,document .getElementById ('test2' )) function speak ( ) { console .log ('i am groot' ) } </script >
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 class Person extends React.Component { static propTypes = { name : PropTypes .string .isRequired , sex : PropTypes .string , age : PropTypes .number , speak : PropTypes .func } static defaultProps = { sex : 'middle' , age : 19 } render ( ) { console .log (this ) const {name, age, sex} = this .props return ( <ul > <li > 姓名:{name}</li > <li > 性别:{sex}</li > <li > 年龄:{age}</li > </ul > ) } }
函数式组件中使用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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="test"></div> <div id="test1"></div> <div id="test2"></div> <script type="text/javascript" src="../js/react.development.js"></script> <script type="text/javascript" src="../js/react-dom.development.js"></script> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/javascript" src="../js/prop-types.js"></script> <script type="text/babel"> // 函数式组件 function Animal(props) { const {name, age, sex} = props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } // 对标签属性进行类型、必要性的限制 Animal.propTypes = { name: PropTypes.string.isRequired, // 限制name必传,且为字符串 sex: PropTypes.string, // 限制sex为字符串 age: PropTypes.number, // 限制age为字数字 speak: PropTypes.func // 限制speak为函数 } // 指定默认标签属性、 Animal.defaultProps = { sex: 'middle', //sex默认值为middle age: 19 // age默认值为19 } // 渲染组件到页面 ReactDOM.render(<Animal name="tommy"/>, document.getElementById('test1')) </script>
3.refs 字符串形式的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 class Dem0 extends React.Component { showData = () => { const {input1} = this .refs alert (input1.value ) } showData2 = () => { console .log (this ) const {input2} = this .refs console .log (input2.value ) alert (input2.value ) } render ( ) { return ( <div > <input ref ="input1" type ="text" placeholder ="点击按钮提示数据" /> <button ref ="button1" onClick ={this.showData} > 点击提示左侧的数据</button > <input ref ="input2" onBlur ={this.showData2} type ="text" placeholder ="失去焦点提示数据" /> </div > ) } }
回调形式的ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Demo1 extends React.Component { showData = () => { const {input1} = this alert (input1.value ) } showData2 = () => { console .log (this ) const {input2} = this alert (input2.value ) } render ( ) { return ( <div > <input ref ={c => this.input1 = c} type="text" placeholder="点击按钮提示数据"/> <button ref ="button1" onClick ={this.showData} > 点击提示左侧的数据</button > <input onBlur ={this.showData2} ref ={c => this.input2 = c} type="text" placeholder="失去焦点提示数据"/> </div > ) } }
回调形式ref调用次数的问题
在使用函数式回调ref时,当组件state更新时,回调中会调用两次,第一次值为null ,第二次为该元素
类的绑定形式的ref:将回调中的方法挂载到当前class上,则会避免这个问题
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 class Demo extends React.Component { state = {isHot :false } showData = () => { const {input1} = this alert (input1.value ) } changeWeather = () => { const {isHot} = this .state this .setState ({isHot :!isHot}) } saveInput = (c ) => { this .input1 = c console .log ("a" ,c) } render ( ){ const {isHot} = this .state return ( <div > <h1 > 今天天气很{isHot ? '炎热' : '凉爽'}</h1 > {/*<input ref ={(currentNode) => {this.input1 = currentNode;console.log('@',currentNode)}} type="text"/>*/} <input ref ={this.saveInput} type ="text" /> <button onClick ={this.showData} > 点我提示输入的数据</button > <button onClick ={this.changeWeather} > 切换天气</button > </div > ) } }
createRef
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Demo1 extends React.Component { myRef = React .createRef () showData2 = () => { console .log (this .myRef ) const {current} = this .myRef alert (current.value ) } render ( ) { return ( <div > <input onBlur ={this.showData2} ref ={this.myRef} type ="text" placeholder ="失去焦点提示数据" /> </div > ) } }
4.属性VS状态 相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
属性能从父组件获取,状态不能
属性可以由父组件修改,状态不能
属性能在内部设置默认值,状态也可以,设置方式不一样
属性不在组件内部修改,状态要在组件内部修改
属性能设置子组件初始值,状态不可以
属性可以修改子组件的值,状态不可以
4.数据渲染 1.条件渲染 1 2 3 { condition?'渲染列表的代码' :'空空如也' }
2.列表渲染 1 2 3 4 5 { data.map (item => <li key ={item.id} > {item.name}</li > ) }
React的高效依赖于所谓的 Virtual-DOM,尽量不碰 DOM。对于列表元素来说会有一个问题:元素可能会在一个列表中改变位置。要实现这个操作,只需要交换一下 DOM 位置就行了,但是React并不知道其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM ),这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识,React 就可以知道这两个元素只是交换了位置,这个标识就是key,是每个元素的唯一标识。
3.dangerouslySetInnerHTML 处于安全的原因,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本。这时候就需要使用dangerouslySetHTML属性,它允许我们动态设置innerHTML
1 2 3 4 5 6 7 8 9 import React , {useState} from 'react' ;export default function Dangerous ( ) { const [data,setData] = useState ("<h2>i am superman~</h2>" ) return ( <div > <div dangerouslySetInnerHTML ={{__html:data}}/ > </div > ) }