React组件基础
-
组件的两种创建方式:函数组件和类组件
-
无状态(函数)组件,负责静态结构展示
-
有状态(类)组件,负责更新组件UI,让页面动起来
-
绑定事件注意this指向问题
-
推荐使用受控组件来处理表单
-
完全利用JS语言的能力创建组件,这是React的思想
使用函数创建组件
function Hello(){ return ( <div>这里是函数组件</div> ) } //const Hello=()=><div>这里是函数组件<div> ReactDOM.render(<Hello/>,document.getElementById("root"))
使用类创建组件
class Hello extends React.Component{ render(){ return <div>这里是类组件</div> //null } } ReactDOM.render(<Hello/>,document.getElementById("root"))
事件绑定
//类组件 class App extends React.Component{ handleClick(){ console.log("单机事件触发") } render(){ return( <button onClick={this.handleClick}>事件触发</button> ) } } export default App //函数组件 function App(){ function handleClick(){ console.log("单机事件触发") } return <button onClick={handleClick}>事件触发</button> } export default App
事件对象
-
可以通过事件处理程序获取到事件对象
-
React中的事件对象叫做:合成事件(对象)
-
合成事件:兼容所有浏览器,无需担心浏览器兼容性问题
class App extends React.Component{ handleClick(e){ //阻止浏览器的行为默认行为 e.preventDefault() console.log("打印跳转去百度") } render(){ return <a href="https://www.baidu.com" onClick={this.handleClick}>跳转到百度</a> } } ReactDOM.render(<App/>,document.getElementById("root"))
有状态组件和无状态组件
-
函数组件又叫做无状态组件,类组件又叫做有状态组件
-
状态(state)即数据
-
函数组件没有自己的状态,只负责数据展示(静)
-
类组件有自己的状态,负责跟新UI,让页面"动"起来
class App extends React.Component{ //constructor(){ // super() // this.state={ // count:0 // } // this.handleClick=this.handleClick.bind(this) //} //简化语法 state={ count:0 } handleClick(){ this.setState({ count:this.state.count+1 }) } render(){ return <div> <h1>数字:{this.state.count}</h1> <button onClick={()=>this.handleClick()}>点击加一</button> { /*<button onClick={()=>{ this.setState({ count:this.state.count+1 }) }}>点击加一</button>*/} </div> } } ReactDOM.render(<App/>,document.getElementById("root"))
表单处理
受控组件
class App extends React.Component{ state={ txt:"" } render(){ return <input value={this.state.txt} onChange={e=>this.setState({ txt:e.target.value })}/> } } ReactDOM.render(<App/>,document.getElementById("root"))
非受控组件(不推荐,直接操作DOM)
class App extends React.Component{ constructor(){ super() this.txtRef=React.createRef() } handleClick(){ console.log(this.txtRef.current.value) } render(){ return ( <div> <input ref={this.txtRef}/> <button onClick={this.handleClick}>获取文本框的值</button> </div> ) } }
组件通讯
组件的props
组件是封闭的,要接收外部数据应该通过props来实现
props的作用:接收传递给组件的数据
传递数据:给组件添加组件
接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据
//函数组件 //2.接收数据 const Hello=(props)=>{ return ( <div>接收到传递来的数据:{props.name}</div> ) } //1.传递数据 ReactDOM.render(<Hello name="Bob"/>,document.getElementById("root"))
//类组件 //2.接收数据 class App extends React.Component{ render(){ return (<div>接收到传递来的数据:{this.props.name}</div>) } } //1.传递数据 ReactDOM.render( <Hello name="Bob" age={19} colors={["red","green","blue"]} fn=()=>{console.log("props可以传递任意类型的数据")} tag={<p>这是一段jsx</p>} ></Hello>, document.getElementById("root"))
proprs的特点:
-
可以给组件传递任意类型的数据
-
props是只读的对象,只能读取属性的值,无法修改对象
-
注意:使用类组件时,如果写了构造函数(constructor),应该将props传递给super(),否则,无法在构造函数中获取到props !
class Hello extends React.Component{ constructor(props){ //推荐将props传递给父类构造函数 super(props) } render(){ //本来就可以拿到,不受任何影响 return (<h1>{this.props.name}</h1>) } } ReactDOM.render(<Hello name="Bob"/>,document.getElementById("root"))
#####
组件通讯的三种方式
-
父组件->子组件
-
子组件->父组件
-
兄弟组件
父组件给子组件传参:
-
父组件提供要传递的state的数据
-
给组件标签添加属性,值为state中的数据
-
子组件中通过props接收父组件中传递的数据
//父组件 class Father extends React.Component{ state={ hisname="塔兹米" } render(){ return (<div> <Child name={this.state.hisname}/> </div>) } }
//子组件 const Child = props =>(<div>{props.name}</div>)
子组件给父组件传参:
-
利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数
-
父组件提供一个回调函数(用于接收数据)
-
将该函数作为属性的值,传递给子组件
-
子组件通过props调用回调函数
-
将子组件的数据作为参数传递给回调函数
//父组件 class Father extends React.Component{ state={ parentmsg:"" } Childmassage=(msg)=>{ console.log(msg) this.setState({ parentmsg:msg }) } render(){ return( <div> 父组件数据:{this.state.parentmsg} <Child kk={this.Childmassage}/> </div> ) } }
//子组件 class Child extends React.Component{ state={ apple:"苹果" } passMsg=()=>{ this.props.kk(this.state.apple) } render(){ return (<div onClick={this.passMsg}></div>) } }
兄弟组件传参:
-
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
-
思想:状态提升
-
公共父组件提升:提供共享状态;提供操作共享状态的方法
-
要通讯的子组件只需通过props接收状态或者操作状态的方法
Context
作用:跨组件传递数据(比如:主题、语言)
使用步骤:
1.调用React.createContext() 创建Provider(提供数据)和Consumer(消费数据)两个组件
const {Provider,Consumer} = React.CreateContext()
2.使用Provider组件作为父节点
3.设置value属性,表示要传递的数据
<Provider value="pink"> <div className="App"> <Child1/> </div> </Provider>
4.调用Consumer组件接收数据
<Consumer> {(data)=><span>data参数表示接收到的数据</span>} </Consumer>
例如:
const {Provider,Consumer} = React.CreateContext() class App extends React.Component{ render(){ return( <Provider value="pink"> <div className="app"> <Node/> </div> </Provider> ) } } const Node=props =>{ return ( <div className="node"> <SubNode/> </div> ) } const SubNode = props =>{ return( <div className="subnode"> <Child/> </div> ) } const Child=props=>{ return( <div className="child"> <Consumer> { data=><span>接收到的数据:{data}</span> } </Consumer> </div> ) }
总结:
-
如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯
-
Context提供了两个组件:Provider和Consumer
-
Provider组件:用来提供数据
-
Consumer组件:用来消费数据
Props深入
children属性
children属性:表示组件标签的子节点。当组件标签有子节点时,props就会有该属性
children属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)
const body=document.querySelector("body") const app=document.createElement("div") app.id="app" body.appendChild(app) class Hello extends React.Component{ render(){ console.log(this.props.children) //这里是子节点 return //这里是子节点 ( <h1>{this.props.children}</h1> ) } } ReactDOM.render(<Hello>这里是子节点</Hello>,document.getElementById("app"))
props校验
对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据
如果传入的数据不对,可能导致组件内部报错
关键问题:组件的使用者不知道明确的错误原因
比如:
//小明创建的组件App function App(props){ const arr=props.colors const lis=arr.map((item,index)=><li key={index}>{item.name}</li>) //很明显小明创建的组件需要传入一个数组,然而小红却传入了一个数字 return ( <ul>lis</ul> ) } //小红使用组件App <App colors={19}/>
-
props校验:允许在创建组件的时候,就指定props的类型、格式等
-
作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性
例如:
App.propsType = { colors:PropTypes.array }
使用步骤
-
安装包prop-types(yarn add prop-types/npm i props-types)
-
导入props-types包
-
使用 组件名.propTypes={}来给组件的props添加校验规则
-
校验规则通过PropsTypes对象来指定
-
例如:
import PropType from "prop-types" function App(props){ return( <h1>{prop.color}</h1> ) } App.propTypes={ colors:PropTypes.array, tag:PropTypes.element, filter:PropTypes.shape({ area:PropTypes.string, price:PropTypes.number }) }
约束规则
-
常见类型:array,bool,func,number,object,string
-
React元素类型:element
-
必填项:isRequired
-
特定结构的对象:shape({})
//常见类型 optionalFunc:PropTypes.func, //必选 requiredFunc:PropTypes.func.isRequired, //特定结构的对象 optionalObjectWithShape:PropTypes.shape({ color:PropTypes.string, fonSize:PropTypes.number })
props的默认值
如果我们需要的值,props中没传入,则可以为props设置默认值
-
场景:分页组件->每页显示条数
-
//例如: function App(props){ return( <div>{props.pageSize}</div> ) } //设置默认值 App.defaultProps={ pageSize:10 } //此处没有传pageSize的值 ReactDOM.render(<App/>,document.getElementById("root"))
组件的生命周期
-
意义:组件的生命周期有助于理解组件的运行方式,完成更复杂的组件功能,分析组件错误原因等
-
组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用卸载的过程
-
生命周期的每个阶段总是伴随着一些方法的调用,这些方法就是生命周期的钩子函数
-
钩子函数的作用:为开发人员在不同阶段操作组件提供时机
-
只要类组件才有生命周期
生命周期的三个阶段
1.创建时(挂载阶段)
-
执行实际:组件创建时(页面加载时)
-
执行顺序:
-
constructor()->render()->componentDidMount
//组件生命周期 class App extends React.Component{ constructor(props){ super(props) console.log("创建之时constructor()")//1 } componentDidMount(){ console.log("挂载完成后componentDidMount")//3 } render(){ console.log("渲染阶段render()")//2 return (<h1>挂载阶段</h1>) } }
-
钩子函数 触发时机 作用 constructor 创建组件时,最先执行 1.初始化state;2.为事件处理程序绑定this render 每次组件渲染都会触发 渲染UI(注意:不能调用setStatus) componentDidMount 组件挂载(完成DOM渲染)后 1.发送网络请求;2.DOM操作;
2.更新时(更新阶段)
-
执行时机:1. setState(); 2. forceUpdate(); 3. 组件接收到新的props
-
说明:以上三者任意一种变化,组件就会重新渲染
-
执行顺序:render-->componentDidUpdate()
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 渲染UI(与挂载阶段是同一个render) |
componentDidUpdate | 组件更新(完成DOM渲染)后 | 1.发送网络请求;2.DOM操作;3.注意:如果要setState()必须放在一个if条件中【因为直接调用setState()更新状态,也会导致递归更新!!】 |
class App extends React.Component{ render(){ console.log("渲染阶段") return ( <h1>更新阶段</h1> ) } componentDidUpdate(prevProps){ console.log("组件更新后") //做法:比较更新前后的props是否相同,来决定是否重新渲染更新 console.log("上一次的props:",prevProps,"当前的props",this.props) if(prevProps.count !== this.props.count){ //this.setState({}) //发送ajax请求 } } }
3.卸载时(卸载阶段)
执行时机:组件从页面消失
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载(从页面消失) | 执行清理工作(比如:清理定时器等等) |
class App extends React.Component{ state={ count:0 } handleClick=()=>{ this.setState({ count:this.state.count+1 }) } render(){ return ( <div> {this.state.count >=3 ? <h1>子组件被卸载了,好可怜!!</h1> : <Child kk={this.state.count}/>} <button onClick={this.handleClick}>加一</button> </div> ) } } class Child extends React.Component{ componentDidMount(){ //开启定时器 this.timerId=setInterval( ()=>{console.log("定时器正在执行") },500) } render(){ return ( <h1>点击次数:{this.props.kk}</h1> ) } //Chid组件被卸载时触发 componentWillUnmount(){ console.log("Child已经消失了") //清理定时器 clearInterval(this.timerId) } }