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 的同步问题 (定时器、原生事件里)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/393647.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL的简单介绍

文章目录 数据库关系型数据库非关系型数据”数据库的概念和用途MySQL数据库服务器、数据库和表的关系数据库的创建和删除表创建表修改常见的数据类型和约束字符串类型日期和时间类型PRIMARY KEY使用AUTO_INCREMENT使用UNIQUE使用FOREIGN KEY使用 SQL语言基础SQL语言简介SQL分类…

C++入门基础知识

在之前我们学习了C语言和初阶数据结构的相关知识&#xff0c;现在已经有了一定的代码能力和对数据结构也有了基础的认识&#xff0c;接下来我们将进入到新的专题当中&#xff0c;这个专题就是C。在C中我们需要花费更大的精力和更长的时间去学习这门建立在C语言基础之上的计算机…

接了一个2000块的小活,大家进来看看值不值,附源码

如题&#xff0c;上周的一天&#xff0c;朋友圈的一个旧友找到了我&#xff0c;说让我帮他开发一个小工具&#xff0c;虽然活不大&#xff0c;但没个几年的全栈经验还不一定能接下来&#xff0c;因为麻雀虽小&#xff0c;涉及的内容可不少&#xff1a; 需求分析 原型设计 详细…

LSPatch制作内置模块应用软件无需root 教你制作内置应用

前言 LSPatch功能非常强大&#xff0c;它是一款基于LSPosed核心的免Root Xposed框架软件。这意味着用户无需进行手机root操作&#xff0c;即可轻松植入内置Xposed模块&#xff0c;享受更多定制化的功能和体验&#xff0c;比如微某内置模块版等&#xff0c;这为那些不想root手机…

vue项目部署在子路径中前端配置

vue.config.JS router/index.js或者是man.js

【开发踩坑】windows查看jvm gc信息

windows查看jvm gc信息 EZ 找出java进程PID 控制面板----搜索任务管理器---- 任务管理器----搜索 java----详细信息 这里PID是4856 cmd jstat gc面板 reference&#xff1a; jstat命令

Llama3.1是AI界的Linux?先部署起来再说!

本文简介 前段时间&#xff0c;Meta 发布了 Llama 3.1&#xff0c;这次带来的中杯、大杯和超大杯3个版本。 从纸面数据来看&#xff0c;Llama 3.1 超大杯已经能跟 GPT-4 Omni、Claude 3.5 Sonnet 分庭抗礼了。 而中杯和大杯更是将同量级的对手摁在地上摩擦。 要知道&#xff0…

常见中间件漏洞(一、Tomcat合集)

目录 一&#xff0e;Tomcat Tomcat介绍 1.1 CVE-2017-12615 影响范围 环境搭建 漏洞复现 1.2 后台弱口令部署war包 漏洞原理 影响版本 环境搭建 1.3 CVE-2020-1938 漏洞原理 影响版本 环境搭建 漏洞复现 一&#xff0e;Tomcat Tomcat介绍 tomcat是一个开源而且…

Docker 环境下使用 Traefik v3 和 MinIO 快速搭建私有化对象存储服务

上一篇文章中&#xff0c;我们使用 Traefik 新版本完成了本地服务网关的搭建。接下来&#xff0c;来使用 Traefik 的能力&#xff0c;进行一系列相关的基础设施搭建吧。 本篇文章&#xff0c;聊聊 MinIO 的单独使用&#xff0c;以及结合 Traefik 完成私有化 S3 服务的基础搭建…

Spring Boot集成protobuf快速入门Demo

1.什么是protobuf&#xff1f; Protobuf&#xff08;Protocol Buffers&#xff09;是由 Google 开发的一种轻量级、高效的数据交换格式&#xff0c;它被用于结构化数据的序列化、反序列化和传输。相比于 XML 和 JSON 等文本格式&#xff0c;Protobuf 具有更小的数据体积、更快…

应急响应-主机安全之文件相关命令(Linux操作系统)

目录 概述常用命令file-探测给定文件的类型选项常用选项组合 stat-查看文件信息find-不止查找文件选项测试操作常用选项 locate-比find好用的查找命令选项常用选项组合 chmod-修改文件权限suidsbit chown、chgrp 修改文件所有者/所在组umask-显示或设置创建文件的权限掩码常用选…

大模型备案全网最详细流程说明【附附件】

本文要点&#xff1a;大模型备案最详细说明&#xff0c;大模型备案条件有哪些&#xff0c;《算法安全自评估报告》模板&#xff0c;大模型算法备案&#xff0c;大模型上线备案&#xff0c;生成式人工智能(大语言模型)安全评估要点&#xff0c;网信办大模型备案。 大模型备案安…

python的多线程

python的threading模块&#xff0c;它提供了丰富的接口来创建和管理线程。 定义一个函数print_numbers&#xff0c;这个函数将由线程执行。在这个函数中&#xff0c;我们使用一个循环来打印数字&#xff0c;并使用time.sleep(1)来模拟每个数字打印之间有1秒的延迟。 在 if __…

win7系统利用定时启动+脚本实现MySQL文件自动备份

前言 最近接到项目&#xff0c;数据量不大但对运行数据的安全性要求极高&#xff0c;为避免因不可抗拒因素导致的数据丢失&#xff0c;选择机械硬盘作为数据存储盘&#xff0c;并使用脚本方式对文件进行备份 一、脚本 下面为自动备份文件的 脚本&#xff0c;可根据自身情况进…

OpenSBI设备树

设备树 在前一启动阶段跳转OpenSBI时&#xff0c;可以将设备树的地址通过参数a1传递过来。 OpenSBI相关的配置&#xff08;opensbi_config&#xff09;也可以添加到设备树节点中&#xff0c;OpenSBI执行时会解析和使用这些配置&#xff0c;并在启动结束时删除该节点&#xff…

只强的Java学习之路8-5

一.搭建mybatis pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.a…

部署Springboot + Vue 项目到远程服务器Windows10系统的详细配置

远程服务器操作系统为Windows系统&#xff0c;Java程序环境&#xff0c;Maven环境都安装有&#xff0c;Mysql ,Redis等都有的前提下 1. mysql数据库导入&#xff0c;非常简单很好操作&#xff0c;这里省略。。比如用HeidiSql 或者Navicat 工具导入数据库 2. 后端javaSpringb…

高职物联网智慧农业实训室建设方案

一、项目概述 随着物联网技术的迅猛发展及其在农业领域的广泛应用&#xff0c;智慧农业已经成为推动农业现代化的关键力量。近年来&#xff0c;国家高度重视物联网技术在农业领域的应用与发展&#xff0c;出台了一系列相关政策支持智慧农业建设。如《数字乡村发展战略纲要》明…

英语疑惑之在树上

在树上&#xff0c;on the tree&#xff0c;我想这个这个介词到底该用in&#xff0c;on or other prep。本来我以为跟on the roof差不多&#xff0c;就是在物体表面&#xff0c;可是百度了一下&#xff0c;可以有on the tree, in the tree, by the tree, at the tree, under th…