当前位置: 首页 > news >正文

React 知识点(二)

文章目录

  • 一、React 组件
  • 二、React 组件通信 - 父子通信
  • 三、React 组件通信 - 子父通信
  • 四、React 组件通信 - 兄弟通信
  • 五、React 组件通信 - 跨组件通信(祖先)
  • 六、结合组件通信案例
  • 七、props-children 属性
  • 八、props-类型校验
  • 九、React 生命周期
  • 十、setState 扩展


一、React 组件

1. 函数组件:使用JS函数创建的组件

注:函数名称首字母必须大写;函数必须要有返回值,如果没有可以返回null

import React from "react";import ReactDom from "react-dom";function Header() {return <div>头部</div>
}const Footer = () => {return <div>底部</div>
}const Loading = () => {const loading = falsereturn loading ? <div>加载中...</div> : null
}
const Div = () => {return (<><Header/><Loading/><Footer/></>)
}ReactDom.render(<Div />,document.getElementById('root'))

也可拆分成多个组件引入

Header.js

import React from 'react';export default function  Header() {return (<div>头部</div>);
};

Footer.js

import React from 'react';const Footer = () => {return (<div>底部</div>);
};export default Footer;

Loading.js

import React from 'react';const Loading = () => {const loading = truereturn loading ? <div>加载中...</div> : null
};export default Loading;

index.js

import React from 'react';import ReactDOM from 'react-dom';import Header from "./components/Header";
import Footer from "./components/Footer";
import Loading from "./components/loading";const App = () => {return (<div><Header /><Loading /><Footer /></div>);
};ReactDOM.render(<App/>,document.getElementById('root'))

2. 类组件:使用class语法创建的组件

ES6 类继承:class 创建类,extends 继承类,可以使用父类的属性和函数

在这里插入图片描述
注:类名首字母大写;必须继承 React.Component 父类;必须有 render 函数,无渲染可返回null

App.js

import React from 'react';
import Header from "./components/Header";
import Loading from "./components/Loading";
import Footer from "./components/Footer";// 创建类组件
class App extends React.Component {render() {return (<><Header /><Loading /><Footer /></>)}
}
// 暴露
export default App;

Header.jsx

import React, {Component} from 'react';export default class Header extends Component {render() {return (<div>Header</div>);}
}

index.js

import React from 'react';import ReactDOM from 'react-dom';import App from './App.js'ReactDOM.render(<App/>,document.getElementById('root'))

3. 组件区分

1)无状态组件

- 组件本身不定义状态,没有生命周期,只负责UI渲染
- React16.8 之前的函数组件都是无状态组件,Hooks 出现后函数组件也可以有状态

2)有状态组件

- 组件本身有独立数据,拥有组件的生命周期,存在交互行为
- class 组件可以定义组件自己的状态,拥有组件的生命周期,是有状态组件

3)区别

- 无状态组件没有维护状态只做渲染,性能较好;有状态组件提供数据和生命周期,能力更强。
- React16.8 之前,组件不需要维护数据只渲染就使用 函数组件 ,有数据和交互使用 类组件你需要去判断。
- React16.8 之后,Hooks 出现给函数提供状态,建议使用函数组件。

4. 定义状态

// 定义 state属性 定义状态 它的值是对象
// 使用 state的时候通过this去访问即可
// 数据发生变化,驱动视图更新
import React, {Component} from 'react';export default class App extends Component {// vue 数据定义在 data中// react 类组件数据定义在 成员属性state中state = {title:'数码产品',list:['电脑','手机','平板']}// render函数是模板渲染函数 组件被使用他会自动调用// render() {//     return (//         <div>//             <p>{this.state.title}</p>//             <ul>//                 {this.state.list.map( (item,index) => <li key={index}>{item}</li>)}//             </ul>//         </div>//     );// }render() {// 解构数据const { title, list} = this.state;return (<div><p>{title}</p><ul>{list.map( (item,index) => <li key={index}>{item}</li>)}</ul></div>);}
}

5. 绑定事件

- 在类中声明事件处理函数,在标签上使用 on+事件名称={处理函数} 的方式绑定事件,事件名称需要遵循 大驼峰 规则。
- 处理函数默认的参数就是事件对象,可以使用事件对象处理默认行为和事件冒泡。
import React, {Component} from 'react';const  styleDes = {color:'red',fontSize:'20px'
}
export default class App extends Component {state = {count:20}handleMouseEnter = () => {console.log('鼠标移入')}handleClick = (event) => {console.log(event,'event')// 阻止默认行为event.preventDefault()}render() {const { count } = this.state;return (<div><div style={ styleDes } onMouseEnter={this.handleMouseEnter}>计数器:{count}</div><hr/><a href='https://www.baidu.com' onClick={this.handleClick}>按钮</a></div>);}
}

在这里插入图片描述

6. this指向问题并解决

- 在事件处理函数中打印 this.state.count 发现报错,this 是个 undefined。(指向的是window)
- 演示函数调用对 this 指向的影响,得出函数谁调 this 就执行谁。
- 找出原因: 处理函数不是通过组件去调用的,导致出现 this 不是组件问题。
- 解决方案:- 1. 模板中使用箭头函数- 2. 定义箭头函数- 3. 模板中使用 bind 绑定
import React, { Component } from 'react'const styleDesc = {color:'red',fontSize:'20px'
}export default class App extends Component {state = {count: 10}handleClick(event){event.preventDefault() // 阻止默认行为console.log(this);  // 打印this   undefined 空!}// 方式2: 【定义箭头函数】 留存this的指向handleClick2 = (event)=>{event.preventDefault() // 阻止默认行为console.log(this);  // 打印this  undefined 空!}handleMouseEnter(){console.log("鼠标移入了");}render() {return (<div>{/* 语法:on事件名={事件函数} */}<p  style={styleDesc} onMouseEnter={this.handleMouseEnter} >计数器:{ this.state.count }</p><div>{/* 方式1:【模板里箭头函数】 真实的事件函数是箭头函数,箭头函数好处是留住this的指向 */}<a href="https://www.baidu.com" onClick={ (e)=> this.handleClick(e) }>按钮1</a>===<a href="https://www.baidu.com" onClick={this.handleClick2}>按钮2</a>==={/* 方式3:【模板里面bind绑定】使用bind方法将函数的this指向给定义好,bind返回新函数,给onClick */}<a href="https://www.baidu.com" onClick={ this.handleClick.bind(this) }>按钮3</a></div></div>)}
}

7. 事件传参并且获取事件对象

- 使用特点:
- 1. 不传递参数,使用方法3
- 2. 传递实参,又要获取事件对象,使用方法2
import React, { Component } from 'react'export default class App extends Component {state = {count: 10}// 最后1位形参就是事件对象handleClick1(num,event){console.log(num);event.preventDefault()console.log(this);}handleClick2(e,num){e.preventDefault()console.log(num);console.log(this);}// 3. 定义事件函数为箭头函数handleClick3 = ()=>{console.log(this);}render() {return (<div>{/* 1. 模板里面bind */}<a href='https://www.baidu.com' onClick={ this.handleClick1.bind(this,10) }>按钮1</a>===={/* 2. 模板里箭头函数, 箭头函数才是真正的事件函数,只不过执行了别的代码 */}<a href='https://www.baidu.com' onClick={ (event)=>this.handleClick2(event,20) }>按钮2</a>===={/* 不能传参! */}<button onClick={  this.handleClick3 }>按钮3,30</button></div>)}
}

8. 类组件 - setstate 使用

在react里 类组件里修改state数据需要 setState(修改对象)。setState函数调用之后render函数执行,模板也就更新,展示出最新的数据
import React, { Component } from 'react'export default class App extends Component {state = {count: 10,user:{name:'Tom',age:20},list:['电脑','手机']}handleClick(){this.setState({count: this.state.count + 1 })}changeArr = () => {this.setState({list:[...this.state.list,'平板']})}changeAge(num){this.setState({user:{...this.state.user,age: this.state.user.age + num}})}render() {return (<div><div>计数器:{this.state.count}</div><br/><button onClick={ () => this.handleClick() }>count+1</button><br/><p>{this.state.list.join('-')}<button onClick={ this.changeArr }>新增</button></p><p>姓名:{this.state.user.name}<br/>年龄:{this.state.user.age}<button onClick={ () => this.changeAge(1) }>增加</button></p></div>)}
}

在这里插入图片描述
9. 类组件 - 受控组件

表单元素的值被 React 中 state 控制,这个表单元素就是受控组件。
如何绑定表单元素,如 input:text input:checkbox
import React, { Component } from 'react'export default class App extends Component {state = {mobile:'13312344556',isChange:true}render() {return (<div><p>手机号:<input type="tel" value={this.state.mobile}></input></p><p><input type="checkbox" checked={this.state.isChange} id="i"></input>同意</p></div>)}
}

在这里插入图片描述
提示需要加上onChange事件

import React, { Component } from 'react'export default class App extends Component {state = {mobile:'13312344556',isChange:true}changeMobile = (event)=>{this.setState({mobile:event.target.value})}changeAgree = (event)=>{this.setState({isChange:event.target.checked})}render() {return (<div>{/* state里面的数据 控制输入框的初始值 一定要用onChange事件修改state里面的数据 */}<p>手机号:<input type="tel" value={this.state.mobile} onChange={ this.changeMobile }></input></p><p><input type="checkbox" checked={this.state.isChange} id="i" onChange={ this.changeAgree }></input>同意</p></div>)}
}

10. 类组件 - 非受控组件

没有通过 state 控制的表单元素,它自己控制自身的值,就是非受控组件。
通过 ref 获取表单元素获取非受控组件的值。
// 1. 通过createRef 创建一个ref对象
// 2. 给元素绑定 ref属性值为创建的ref对象
// 3. 通过ref对象的current获取元素 再获取值
import React, {Component, createRef} from 'react'export default class App extends Component {// 1mobilRef = createRef()agreeRef = createRef()submitButton = () => {// 3console.log(this.mobilRef.current.value)console.log(this.agreeRef.current.checked)}render() {return (<div>// 2{/* 输入数据完全不受控制 最终通过获取DOM或组件实例来读取对应的数据内容 */}<p>手机号:<input type="tel" ref={ this.mobilRef }></input></p><p><input type="checkbox"  id="i" ref={ this. agreeRef }></input>同意</p><br/><button onClick={ this.submitButton }>提交</button></div>)}
}

案例

public/index.html

<link href="https://at.alicdn.com/t/font_2998849_vtlo0vj7ryi.css" rel="stylesheet"/>

App.js

import { Component } from 'react';
import Comment from './components/Comment.jsx';
class App extends Component {render() {return (<><Comment /></>);}
}
export default App;

index.js

import React from 'react';import ReactDOM from 'react-dom';import App from './App.js'ReactDOM.render(<App/>,document.getElementById('root'))

src/components/index.css

body {margin: 0;
}
.comments {background-color: #121212;color: #eee;padding: 24px;width: 1000px;margin: 0 auto;
}
.comm-head {color: #eee;font-size: 24px;line-height: 24px;margin-bottom: 24px;
}
.comm-head sub {font-size: 14px;color: #666;margin-left: 6px;bottom: 0.2em;position: relative;
}.comm-head span {display: inline-block;line-height: 1;padding: 5px 16px;font-size: 14px;font-weight: normal;border-radius: 12px;background-color: rgba(255, 255, 255, 0.1);color: #999;cursor: pointer;margin-left: 30px;
}
.comm-head span:hover,
.comm-head span.active {color: #61f6ff;
}.comm-list {list-style: none;padding: 0;
}
.comm-item {display: flex;margin-bottom: 24px;
}
.comm-item .avatar {width: 48px;height: 48px;line-height: 48px;border-radius: 24px;display: inline-block;cursor: pointer;background-position: 50%;background-size: 100%;background-color: #eee;
}
.comm-item .info {padding-left: 16px;
}
.comm-item .info p {margin: 8px 0;
}
.comm-item .info p.name {color: #999;
}
.comm-item .info p.vip {color: #ebba73;
}
.comm-item .info p.vip img {width: 14px;vertical-align: baseline;margin-left: 5px;
}
.comm-item .info p.time {color: #666;font-size: 14px;display: flex;align-items: center;
}.comm-item .info .iconfont {margin-left: 20px;position: relative;top: 1px;cursor: pointer;
}
.comm-item .info .iconfont.icon-collect-sel {color: #ff008c;
}
.comm-item .info .del {margin-left: 20px;cursor: pointer;
}
.comm-item .info .del:hover {color: #ccc;
}.comm-input {border-radius: 6px;padding: 18px;background-color: #25252b;
}
.comm-input textarea {border: 0;outline: 0;resize: none;background: transparent;color: #999;width: 100%;font-family: inherit;height: auto;overflow: auto;
}
.comm-input .foot {display: flex;justify-content: flex-end;justify-items: center;
}
.comm-input .foot .word {line-height: 36px;margin-right: 10px;color: #999;
}
.comm-input .foot .btn {background-color: #ff008c;font-size: 14px;color: #fff;line-height: 36px;text-align: center;border-radius: 18px;padding: 0 24px;cursor: pointer;user-select: none;
}

src/components/Comment.js

import React, { Component } from 'react';
import './index.css';export default class Comment extends Component {state = {// 当前用户user: {name: '清风徐来',vip: true,avatar: 'https://static.youku.com/lvip/img/avatar/310/6.png',},// 评论列表comments: [{id: 102,name: '__RichMan',avatar: 'https://r1.ykimg.com/051000005BB36AF28B6EE4050F0E3BA6',content:'这阵容我喜欢😍靳东&闫妮,就这俩名字,我就知道是良心剧集...锁了🔒',time: '2021/10/12 10:10:23',vip: true,collect: false,},{id: 101,name: '糖蜜甜筒颖',avatar:'https://image.9xsecndns.cn/image/uicon/712b2bbec5b58d6066aff202c9402abc3370674052733b.jpg',content:'突围神仙阵容 人民的名义第三部来了 靳东陈晓闫妮秦岚等众多优秀演员实力派 守护人民的财产 再现国家企业发展历程',time: '2021/09/23 15:12:44',vip: false,collect: true,},{id: 100,name: '清风徐来',avatar: 'https://static.youku.com/lvip/img/avatar/310/6.png',content:'第一集看的有点费力,投入不了,闫妮不太适合啊,职场的人哪有那么多表情,一点职场的感觉都没有',time: '2021/07/01 00:30:51',vip: true,collect: false,},],// 评论内容content:''}contentChange = (event) => {// 输入的最新值做判断是否大于100if(event.target.value.length > 100) return;this.setState({content:event.target.value})}addComment = () => {const len = this.state.comments.lengthconst newData = {...this.state.user,content: this.state.content,collect: false,id:len?this.state.comments[0].id*1+1:1,time: (new Date()).toLocaleString()}this.setState({comments:[newData,...this.state.comments],content:''})}delectComment(id) {if(!window.confirm('确定要删除吗?')) return;this.setState({comments:this.state.comments.filter(item => item.id !== id)})}Collect(id){const newArr = this.state.comments.map(item => {if(item.id == id) item.collect = !item.collectreturn item})this.setState({comments: newArr})}render() {const { comments,content,user } = this.statereturn (<div className="comments">{/*  输入框区域 */}<h3 className="comm-head">评论</h3><div className="comm-input"><textarea placeholder="爱发评论的人,运气都很棒" value={ content } onChange={ this.contentChange }></textarea><div className="foot"><div className="word">{ content.length }/100</div><div className="btn" onClick={ this.addComment }>发表评论</div></div></div>{/*  头部区域 */}<h3 className="comm-head">热门评论<sub>({comments.length})</sub></h3>{/*  评论列表区域 */}<ul className="comm-list">{ comments.map(item => {return (<li className="comm-item" key={item.id}><div className="avatar" style={{ backgroundImage: `url(${item.avatar})` }}></div><div className="info"><p className="name vip">{item.name}{ item.vip ? <img src="https://gw.alicdn.com/tfs/TB1c5JFbGSs3KVjSZPiXXcsiVXa-48-48.png" /> : ''}</p><p className="time">{ item.time }{/*<span className={ item.collect ? 'iconfont icon-collect-sel' : 'iconfont icon-collect'} ></span>*/}<span className={ `iconfont icon-collect${item.collect ? '-sel' : ''}`} onClick={ ()=>this.Collect(item.id) }></span>{ item.name == user.name ? <span className="del" onClick={ this.delectComment.bind(this,item.id)}>删除</span> : ''}</p><p>{ item.content }</p></div></li>)})}</ul></div>);}
}

二、React 组件通信 - 父子通信

- 使用组件的时候通过属性绑定数据,在组件内部通过 props 获取即可。
- 单向数据流:父组件传递数据给子组件,父组件更新数据子组件自动接收更新后数据,子组件是不能修改数据的。
- 可以传递任意数据(字符串,数字,布尔,数组,对象,函数,JSX(插槽))
- 如果传递的数据是数组里面的每项值的话 可以不用一个一个写 直接 {...数组名称}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、React 组件通信 - 子父通信

- 父组件通过props将修改数据的方法,传递给子组件,让子组件调用
- 父组件传递给子组件的方法需要用箭头函数,不让this指向变化

在这里插入图片描述

四、React 组件通信 - 兄弟通信

- 状态提升:将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态和修改状态的方法
- 需要通讯的组件通过 props 接收状态和函数即可

在这里插入图片描述

在这里插入图片描述

五、React 组件通信 - 跨组件通信(祖先)

- 一个范围,只要在这个范围内,就可以跨级组件通讯。(不需要 props 层层传递)
- 使用 context 完成跨级组件通讯。

在这里插入图片描述
在这里插入图片描述

六、结合组件通信案例

在这里插入图片描述
index.js

import React from 'react';
import ReactDOM from 'react-dom';import App from './App.js'
import './index.css'ReactDOM.render(<App/>,document.getElementById('root'))

index.css

body {margin: 0;
}
.comments {background-color: #121212;color: #eee;padding: 24px;width: 1000px;margin: 0 auto;
}
.comm-head {color: #eee;font-size: 24px;line-height: 24px;margin-bottom: 24px;
}
.comm-head sub {font-size: 14px;color: #666;margin-left: 6px;bottom: 0.2em;position: relative;
}.comm-head span {display: inline-block;line-height: 1;padding: 5px 16px;font-size: 14px;font-weight: normal;border-radius: 12px;background-color: rgba(255,255,255,0.1);color: #999;cursor: pointer;margin-left: 30px;
}
.comm-head span:hover , .comm-head span.active{color: #61f6ff;
}.comm-list {list-style: none;padding: 0;
}
.comm-item {display: flex;margin-bottom: 24px;
}
.comm-item .avatar {width: 48px;height: 48px;line-height: 48px;border-radius: 24px;display: inline-block;cursor: pointer;background-position: 50%;background-size: 100%;background-color: #eee;
}
.comm-item .info {padding-left: 16px;
}
.comm-item .info p {margin: 8px 0;
}
.comm-item .info p.name {color: #999;
}
.comm-item .info p.vip {color: #ebba73;
}
.comm-item .info p.vip img {width: 14px;vertical-align: baseline;margin-left: 5px;
}
.comm-item .info p.time {color: #666;font-size: 14px;display: flex;align-items: center;
}.comm-item .info .iconfont {margin-left: 20px;position: relative;top: 1px;cursor: pointer;
}
.comm-item .info .iconfont.icon-collect-sel {color: #ff008c;
}
.comm-item .info .del {margin-left: 20px;cursor: pointer;
}
.comm-item .info .del:hover {color: #ccc;
}.comm-input {border-radius: 6px;padding: 18px;background-color: #25252b;
}
.comm-input textarea {border: 0;outline: 0;resize: none;background: transparent;color: #999;width: 100%;font-family: inherit;height: auto;overflow: auto;
}
.comm-input .foot {display: flex;justify-content: flex-end;justify-items: center;
}
.comm-input .foot .word {line-height: 36px;margin-right: 10px;color: #999;
}
.comm-input .foot .btn {background-color: #ff008c;font-size: 14px;color: #fff;line-height: 36px;text-align: center;border-radius: 18px;padding: 0 24px;cursor: pointer;user-select: none;
}

app.js

import React, {Component} from 'react';
import CommentHeader from "./components/CommentHeader";
import CommentInput from "./components/CommentInput";
import CommentList from "./components/CommentList";export default class App extends Component {state = {user: {name: '清风徐来',vip: true,avatar: 'https://static.youku.com/lvip/img/avatar/310/6.png',},// 评论列表comments: [{id: 100,name: '__RichMan',avatar: 'https://r1.ykimg.com/051000005BB36AF28B6EE4050F0E3BA6',content:'这阵容我喜欢😍靳东&闫妮,就这俩名字,我就知道是良心剧集...锁了🔒',time: '2021/10/12 10:10:23',vip: true,collect: false,},{id: 101,name: '糖蜜甜筒颖',avatar:'https://image.9xsecndns.cn/image/uicon/712b2bbec5b58d6066aff202c9402abc3370674052733b.jpg',content:'突围神仙阵容 人民的名义第三部来了 靳东陈晓闫妮秦岚等众多优秀演员实力派 守护人民的财产 再现国家企业发展历程',time: '2021/09/23 15:12:44',vip: false,collect: true,},{id: 102,name: '清风徐来',avatar: 'https://static.youku.com/lvip/img/avatar/310/6.png',content:'第一集看的有点费力,投入不了,闫妮不太适合啊,职场的人哪有那么多表情,一点职场的感觉都没有',time: '2021/07/01 00:30:51',vip: true,collect: false,},],active:'default' // default 默认id time时间}changeActive = (str) => {this.setState({active:str})}addComment = (comment) => {let newComment = {...this.state.user,collect: false,time: new Date().toLocaleString(),id:Math.ceil(Math.random()*100),content:comment}this.setState({comments:[...this.state.comments,newComment]})}// 点赞delCollect = (id) => {let newComments = this.state.comments.map(item => {if(item.id === id) {item.collect = !item.collect}return item})this.setState({comments:newComments})}// 删除delComment = (id) => {if(!window.confirm('确定要删除吗?')) return;let newArr = this.state.comments.filter(item => item.id !== id)this.setState({comments:newArr})}render() {const { user, comments,active } = this.statereturn (<div className="comments"><CommentInput addComment={ this.addComment } /><CommentHeader len={ comments.length } changeActive={ this.changeActive } active={active} /><CommentList delComment={ this.delComment } delCollect={ this.delCollect } comments={ comments } active={active} user={user}/></div>);}
}

CommentInput.js

import React, {Component} from 'react';export default class CommentInput extends Component {state = {content:''}changeInput = (e) => {if(e.target.value.length > 100) return// if(e.target.value.length === 0) return alert('请输入内容');this.setState({content:e.target.value})}submitContent = () => {if(this.state.content.length === 0) return alert('请输入内容');this.props.addComment(this.state.content)this.setState({content:''})}render() {const { content } = this.statereturn (<>{/*  输入框区域 */}<h3 className="comm-head">评论</h3><div className="comm-input"><textarea value={ content } placeholder="爱发评论的人,运气都很棒" onChange={ this.changeInput }></textarea><div className="foot"><div className="word">{content.length}/100</div><div className="btn" onClick={ this.submitContent }>发表评论</div></div></div></>);}
}

CommentHeader.js

import React, {Component} from 'react';export default class CommentHeader extends Component {render() {const { len,active,changeActive } = this.propsreturn (<>{/*  头部区域 */}<h3 className="comm-head">热门评论<sub>({len})</sub><span onClick={ () => changeActive('default')} className={ active === 'default' ? 'active' : 'default' } > 默认</span><span onClick={ () => changeActive('time')} className={ active === 'time' ? 'active' : 'default' } > 时间</span></h3></>);}
}

CommentList.js

import React, {Component} from 'react';export default class CommentList extends Component {render() {const { comments,active,user,delCollect,delComment } = this.props// 处理时间 使用time2为了方便后面展示comments.map(item => {item.time2 = new Date(item.time).getTime()})const newList = [...comments]// 按照id排序if(active === 'default') {newList.sort((a,b) => b.id-a.id)}// 按照time排序if(active === 'time') {newList.sort((a,b) => b.time2-a.time2)}return (<>{/*  评论列表区域 */}<ul className="comm-list">{newList.map(item=>{return (<li className="comm-item" key={item.id}><div className="avatar" style={ {backgroundImage:`url(${item.avatar})`}}></div><div className="info"><p className="name vip">{item.name}{item.vip ? <img src="https://gw.alicdn.com/tfs/TB1c5JFbGSs3KVjSZPiXXcsiVXa-48-48.png" /> : ''}</p><p className="time">{item.time}<span className={`iconfont icon-collect${item.collect?'-sel':''}`} onClick={()=>delCollect(item.id)}></span>{ item.name === user.name ? <span className="del" onClick={ () => delComment(item.id)}>删除</span> : ''}</p><p>{item.content}</p></div></li>)})}</ul></>);}
}

七、props-children 属性

- 组件标签的子节点(标签之间的内容,插槽),可以是任意值(文本,React元素,组件,函数)
- react实现插槽的2种方式
- 1. props传递jsx片段
- 2. props.children 读取组件之间的内容

在这里插入图片描述

八、props-类型校验

- 导入 import PropTypes from "prop-types";
- 使用 组件名.propTypes = {'props属性':'props校验规则’} 进行类型约定
- PropTypes 包含各种规则
import React from "react";
// 1. 导入 prop-types
import PropTypes from "prop-types";export default function Hello(props) {return (<><div>Hello</div><span>{props.arr.map((item,index) => {return <span key={index}>{item}</span>})}</span></>)
}
// 2.校验规则
Hello.prototype = {arr:PropTypes.array
}
// 校验规则
list.prototype = {// 语法: 属性名:PropTypes.类型函数.isRequired// 规定optionalFunc属性值必须是函数optionalFunc:PropTypes.func,// 规定requiredFunc属性值必须是函数且必须传入requiredFunc:PropTypes.func.isRequired,// 规定size属性值的类型可以是数组或字符串 size:PropTypes.oneOfType([PropTypes.number,PropTypes.string]),// 个性化定义person:PropTypes.shape({name:PropTypes.string,age:PropTypes.number,say:PropTypes.func})
}
- props默认值设置 : 组件.defaultProps ={属性名:默认值}
import React from 'react'
import PropTypes from "prop-types";// 也可以用 参数默认值 来实现 pageNum=1
const Pagination = ({pageSize,pageNum=1}) => {return (<div>Pagination<br />pageSize 的默认值是: {pageSize }<br />pageNum 的默认值是:{pageNum}</div>)
}
// 定义默认值
Pagination.defaultProps = {pageSize:10
}export default Pagination
- 静态属性
- 类名.属性 = 新值      给莫格类定义静态属性
- 类名.方法名 = 函数    给某个类定义静态方法
- 在类组件中通过 static propTypes={} 定义props校验规则 
- static defaultProps ={} 定义props 默认值 
import React,{Component} from "react";
// 1. 导入 prop-types
import PropTypes from "prop-types";export default class Hello extends Component {// 2. 定义静态属性 定义校验类型static propTypes = {// oneOf校验唯一 二选一sex:PropTypes.oneOf(['男','女']).isRequired}// 3. 定义静态属性 默认值static defaultProps = {sex:'男'}render() {return (<div>{this.props.sex}</div>);}
}// 也可以写在外面
// Hello.propTypes = {
//     arr:PropTypes.oneOf(['男','女']).isRequired
// }
// Hello.defaultProps = {
//     sex: '男'
// }

九、React 生命周期

- 生命周期:是从创建到最后消亡的过程
- 类组件有生命周期  函数组件没有生命周期除非使用Hooks

在这里插入图片描述

在这里插入图片描述

1.Mounting(挂载):已插入真实 DOM

钩子函数触发时机作用
constructor创建组件时 最先执行1.初始化state 2.创建ref 3. 使用bind解决this指向问题
render每次组件渲染都会触发渲染UI 不能调用setState()
componentDidMount组件挂载后1.发送网络请求 2.DOM操作
import React, {Component, createRef} from "react";export default class Hello extends Component {// 1. 挂载阶段 初始化constructor(){super()this.state = {}this.ipt = createRef()console.log("挂载阶段 初始化")}// 2. 挂载阶段 渲染render() {console.log("挂载阶段 渲染")return (<div>Hello</div>);}// 3. 挂载阶段 渲染完成componentDidMount() {console.log("挂载阶段 渲染完成")}
}

2.Updating(更新):正在被重新渲染

- componentDidUpdate(): 在更新后会被立即调用。

在这里插入图片描述
在这里插入图片描述
3.Unmounting(卸载):已移出真实 DOM

- componentWillUnmount(): 在组件卸载及销毁之前直接调用。

在这里插入图片描述
在这里插入图片描述

1. 挂载期 constructor  ->  render  -> componentDidMount
2. 更新期 render -> componentDidUpdate
3. 销毁期 componentWillUnmount
4. 执行顺序 父组件(constructor) -> 父组件(render)  -> 子组件(constructor)  -> 子组件(render) -> 子组件(componentDidMount) -> 父组件(componentDidUpdate)
5. 阻止组件更新:shouldComponentUpdate(nextProps, nextState)
shouldComponentUpdate() 方法会返回一个布尔值,指定 React 是否应该继续渲染,默认值是 true, 即 state 每次发生变化组件都会重新渲染。
shouldComponentUpdate() 的返回值用于判断 React 组件的输出是否受当前 state 或 props 更改的影响,当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。

在这里插入图片描述
在这里插入图片描述

十、setState 扩展

- 调用 setstate 时,将要更新的状态对象,放到一个更新队列中暂存起来(没有立即更新)
- 如果多次调用 setState 更新状态,状态会进行合并,后面覆盖前面
- 等到所有的操作都执行完毕,React 会拿到最终的状态,然后触发组件更新
import React, {Component} from 'react'
export default class Hi extends Component {state = {count: 0}handleClick = () => {// 【重要点】:如果多次调用 setState 更新状态,状态会进行合并,后面覆盖前面this.setState({count: this.state.count+100})this.setState({count: this.state.count+1})// 【重要点】:setState是异步操作!console.log(this.state.count)  // 打印0}render() {console.log('render')return (<div><div>Hi组件:{this.state.count}</div><button onClick={this.handleClick}>体现“异步”和合并</button></div>)}
}
- 使用 setState((prevState) => {}) 可以解决多次调用状态依赖问题
- 使用 setState(updater[,callback]) 在状态更新后立即执行某个操作
import React, {Component} from 'react'
export default class Hi extends Component {state = {count: 0}handleClick = () => {// this.setState({count: this.state.count+1})// this.setState({count: this.state.count+1})// this.setState({count: this.state.count+1})// 以上写法会合并更新,本质只会执行最后1个// 语法1: 合并更新,采用最后1个// this.setState({//   count:this.state.count+1  // count的更新是依赖于之前的state状态!// })// 语法2: 在之前的状态上进行,也就是多个都会调用!// 语法格式 this.setState( (之前的state)=>({ 新数据 }) )this.setState(oldState=>{return {count:oldState.count+1}})this.setState(oldState=>({ count:oldState.count+1 }),()=>{console.log("setState执行完毕,最新的count数据是:"+this.state.count);})this.setState(oldState=>({ count:oldState.count+1 }),()=>{console.log("setState执行完毕,最新的count数据是:"+this.state.count);})// 总结: this.setState(对象/函数,回调函数)}render() {return (<div><div>Hi组件:{this.state.count}</div><button onClick={this.handleClick}>setState串联更新数据</button></div>)}
}
- setstate本身并不是一个异步方法,其之所以会表现出一种“异步"的形式,是因为react框架本身的一个性能优化机制
- React会将多个setstate的调用合并为一个来执行,也就是说,当执行setstate的时候,state中的数据并不会马上更新
- setstate如果是在react的生命周期中或者是事件处理函数中,表现出来为:延迟合并更新(“异步更新”)
- setstate如果是在setTimeout/setlnterval或者原生事件中,表现出来是:立即更新(“同步更新”)★ 在react事件函数或者生命周期函数表现“异步",在定时器或者原生事件中表现同步
import React, {Component} from 'react'export default class Demo extends Component {state = {count: 0}handleClick = () => {// 合成事件的处理函数 or 生命周期构造函数// this.setState({count: this.state.count+1})// this.setState({count: this.state.count+1})// 表现异步setTimeout(() => {this.setState({count: this.state.count+1})this.setState({count: this.state.count+1})}, 0);// 表现同步}render() {console.log('render')return (<div><div>Demo组件:{this.state.count}</div><button onClick={this.handleClick}>同步OR异步</button></div>)}
}
- 总结 : setState (类组件)
- 作用: 用于来修改state里面的数据;一旦调用 render函数重新执行!
- 使用: this.setState(函数/对象,回调函数)   回调函数里面可以获取最终最新的state数据
- 注意点:
- 1. this.setState(对象)   多个写法调用, 合并,最终取最后一个setState
- 2. this.setState( oldState=>({新数据}) )   多个写法调用,以此都会执行
- 3. setState 的异步问题 (生命周期、react事件函数里)  解决方法: 第二个参数回调函数里读最新值
- 4. setState 的同步问题 (定时器、原生事件里)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • oracle 判断某个字段包含某几个字符like或INSTR
  • 基于LQR算法的机器人轨迹跟踪控制详解
  • MYSQL 5.7.36 等保 建设记录
  • RGB和HSL是两种不同的颜色表示模型,每种模型都有其特定的用途和含义。
  • InfluxDB Studio 下载,时序数据库Windows图形界面操作
  • C++:智能指针了解
  • Redis 实现消息队列
  • 拥抱变革:旗晟智能巡检机器人系统重塑高风险行业巡检模式
  • 【算法刷题】合并两个有序链表、获取链表的中间节点、反转链表
  • 【面试经验】24届前端校招 字节、阿里、美团、快手、腾讯面试经验汇总
  • 【扒代码】图像数据 Transformer
  • Eclipse插件之Java Dependency Viewer(显示类和包的关系图)
  • 日志Log程序(C++)
  • 深度学习每周学习总结N6:使用Word2vec实现文本分类
  • Spring Cloud全解析:注册中心之zookeeper注册中心
  • hexo+github搭建个人博客
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • js对象的深浅拷贝
  • Selenium实战教程系列(二)---元素定位
  • 从零开始的无人驾驶 1
  • 基于web的全景—— Pannellum小试
  • 前端自动化解决方案
  • 前嗅ForeSpider教程:创建模板
  • 通信类
  • 阿里云重庆大学大数据训练营落地分享
  • 回归生活:清理微信公众号
  • ​secrets --- 生成管理密码的安全随机数​
  • # centos7下FFmpeg环境部署记录
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (2024,RWKV-5/6,RNN,矩阵值注意力状态,数据依赖线性插值,LoRA,多语言分词器)Eagle 和 Finch
  • (4)logging(日志模块)
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • (转)原始图像数据和PDF中的图像数据
  • .env.development、.env.production、.env.staging
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET中 MVC 工厂模式浅析
  • /bin/bash^M: bad interpreter: No such file or directory
  • ::before和::after 常见的用法
  • :not(:first-child)和:not(:last-child)的用法
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • @PostConstruct 注解的方法用于资源的初始化
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [ChromeApp]指南!让你的谷歌浏览器好用十倍!
  • [E链表] lc83. 删除排序链表中的重复元素(单链表+模拟)
  • [flutter]一键将YAPI生成的api.json文件转为需要的Dart Model类的脚本
  • [Git 1]基本操作与协同开发
  • [git]git命令如何取消先前的配置
  • [Java][算法 双指针]Day 02---LeetCode 热题 100---04~07
  • [jQuery]div滚动条回到最底部
  • [Linux]:权限