React 知识点(二)

news2025/4/14 8:18:43

文章目录

  • 一、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 = false
    return 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 = true
    return 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 {
    // 1
    mobilRef = createRef()
    agreeRef = createRef()
    submitButton = () => {
    	// 3
        console.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) => {
    // 输入的最新值做判断是否大于100
    if(event.target.value.length > 100) return;
    this.setState({
      content:event.target.value
    })
  }
  addComment = () => {
    const len = this.state.comments.length
    const 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.collect
      return item
    })
    this.setState({
      comments: newArr
    })
  }
  render() {
    const { comments,content,user } = this.state
    return (
        <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.state
        return (
            <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.state
        return (
            <>
                {/*  输入框区域 */}
                <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.props
        return (
            <>
                {/*  头部区域 */}
                <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.coloradmin.cn/o/1988086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

https证书怎么申请?

申请SSL证书的步骤可以因不同的证书颁发机构&#xff08;CA&#xff09;和证书类型&#xff08;如DV SSL、OV SSL、EV SSL&#xff09;而有所差异。以下是一个通用的SSL证书申请流程&#xff0c;以供参考&#xff1a; 1. 选择SSL证书类型 首先&#xff0c;需要根据您的需求选…

SQLE:你的SQL全生命周期质量管理平台

SQLE&#xff1a;你的SQL全生命周期质量管理平台 在数据库管理领域&#xff0c;总有那么几个难题让人头疼。今天要介绍的SQLE&#xff0c;就是解决这些问题的利器。它不仅支持多种数据库&#xff0c;还能在事前控制、事后监督、标准发布等场景中大显身手。本文将为你详细介绍SQ…

【学习方法】高效学习因素 ② ( 学习动机 | 内在学习动机 | 外在学习动机 | 外在学习动机的调整方向 | 保护学习兴趣 | 高考竞争分析 )

文章目录 一、高效学习的其它因素 - 学习动机1、学习动机2、内在学习动机3、外在学习动机4、外在学习动机的问题所在5、外在学习动机的调整方向6、保护学习兴趣7、高考竞争分析 上一篇博客 【学习方法】高效学习因素 ① ( 开始学习 | 高效学习因素五大因素 | 高效学习公式 - 学…

二十八、【人工智能】【机器学习】- 隐马尔可夫模型 (Hidden Markov Models, HMMs)

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

SQL时间盲注

目录 1.时间盲注 2使用场景 3.步骤 3.1判断注入点 3.2爆数据库名 3.3爆表名 3.4爆字段名 3.5查询数据 1.时间盲注 时间盲注是指基于时间的盲注&#xff0c;也叫延时注入&#xff0c;根据页面的响应时间来判断是否存在注入。 2使用场景 页面没有回显位置&#xff08;…

Lumina-mGPT图像生成;清华大学研发的首个开源预训练文本生成视频模型;Medical SAM 2实现2D和3D医学图像分割

✨ 1: Lumina-mGPT Lumina-mGPT是一种多模态自回归模型&#xff0c;尤其擅长将文本描述生成灵活逼真的图像。 Lumina-mGPT是一系列多模态自回归模型&#xff0c;能够处理多种视觉和语言任务&#xff0c;其中尤为擅长从文本描述中生成灵活的真实感图像。该模型家族由Alpha-VLL…

【EtherCAT】Windows+Visual Studio配置SOEM主站——静态库配置+部署

目录 一、准备工作 1. Visual Studio 2022 2. Npcap 1.79 3. SOEM源码 二、静态库配置 1. 修改SOEM源码配置 2. 编译SOEM源码 3. 测试 三、静态库部署 1. 新建Visual Studio工程 2. 创建文件夹 3. 创建主函数 4. 复制静态库 5. 复制头文件 6. 配置头文件…

[Java]栈--Stack

前言 熟悉Class Stack. 栈 关于栈—笔者的C语言描述 java.util包有Stack集合类. JDK17的Stack源码非常简单,能相对轻易看懂. 我们能用Stack类来充当栈,Java框架中LinkedList(双向链表)实现了双端队列(Deque),也能当作栈使用. Stack类是基于数组实现. public Stack<E>…

腾讯云AI代码助手:AI辅助编写测试用例,测试从此不求人

目录 引言 开发环境介绍 实例&#xff1a;编写测试用例 帮助与提升 建议 结语 引言 在软件开发过程中&#xff0c;编写测试用例是确保代码质量的重要环节。然而&#xff0c;对于新手编程学习者来说&#xff0c;撰写高质量的测试用例可能是一个巨大的挑战。AI 代码助手作为一个智…

基于Android aosp系统的云手机chromium浏览器定制

chromium定制浏览器 chromium源码下载get属性c或者c层常见指纹检测指标 chromium源码下载 最新有点时间研究了一些 网上定制chromium 浏览器的方案&#xff0c;大部分都是基于window的&#xff0c; 修改点无非以下监测点。自己本身做Android系统开发&#xff0c;就想着能否做一…

【AI大模型】Langchain探秘之旅:深入Prompt世界,解锁多种PromptTemplate的实战奇技!

文章目录 前言一、Langchain 概述1. 概述2. 应用场景3. 案例 二、Prompt1.Prompt 介绍2.PromptTemplatea.PromptTemplate 案例单个参数多个参数多个参数-结合字典解包 b.使用jinja2与f-string来实现提示词模板格式化 3.ChatPromptTemplatea.ChatPromptTemplate 介绍b.ChatPromp…

2024年8月一区SCI-海市蜃楼优化算法Fata morgana algorithm-附Matlab免费代码

引言 本期介绍了一种基于地球物理的高效优化方法名为海市蜃楼优化算法Fata morgana algorithm&#xff0c;FATA的元启发式算法。通过模拟海市蜃楼的形成过程&#xff0c;FATA分别设计了海市蜃楼滤光原理(MLF)和光传播策略(LPS)。该成果于2024年8月最新上线在JCR 1区&#xff0…

C#重要知识归纳总结

C#教程 C# 结构体&#xff08;Struct&#xff09; | 菜鸟教程C# 结构体&#xff08;Struct&#xff09; 在 C# 中&#xff0c;结构体&#xff08;struct&#xff09;是一种值类型&#xff08;value type&#xff09;&#xff0c;用于组织和存储相关数据。 在 C# 中&#xff0c…

学习c语言第二十二天(c语言文件操作)

1. 为什么使用文件&#xff1f; 我们前面学习结构体时&#xff0c;写了通讯录的程序&#xff0c;当通讯录运行起来的时候&#xff0c;可以给通讯录中增加、删除数据&#xff0c;此时数据是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯录中的数据自然就不存在了&…

工地人员定位系统的功能和作用

工地人员定位系统是一种基于实时定位技术的管理系统&#xff0c;旨在为施工单位提供安全、高效的管理服务。该系统可以对工人进行实时监控与管理&#xff0c;极大地提高了工地施工安全性和工作效率&#xff0c;降低了工伤事故频率。通过使用该系统&#xff0c;施工单位能够实时…

解决mqtt在线或离线监听问题的多种方法

目录 前言 优缺点&#xff1a; 1.创建 DeviceMqttCallBacked类&#xff0c;实现 MqttCallbackExtended 接口&#xff0c;添加带cleintId参数的构造方法。 2. 在Mqtt连接成功之后&#xff0c;设置回调类。 3.执行上述代码&#xff0c;查看emqx界面 4.从emqx上踢除设备 5…

html+css 实现hover酷炫发光按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

“5G+Windows”推动全场景数字化升级:美格智能5G智能模组SRM930成功运行Windows 11系统

操作系统作为连接用户与数字世界的桥梁&#xff0c;在数字化迅速发展的时代扮演着至关重要的角色&#xff0c;智能设备与操作系统的协同工作&#xff0c;成为推动现代生活和商业效率的关键力量。其中&#xff0c;Windows系统以其广泛的应用基础和强大的兼容性成为全球最广泛使用…

服务器启动jar包的时候报”no main manifest attribute“异常(快捷解决)

所以,哥们,又出现问题咯.没事,我也出现了,哈哈哈哈哈,csdn感觉太麻烦了,所以搞了一篇这个. 没得事,往下看,包解决的. 希望可以帮助到各位&#xff0c;感谢阅览&#xff01; 小手点个赞&#xff0c;作者会乐烂哈哈哈哈哈哈&#x1f606;&#x1f606;&#x1f606;&#x1f606…

Linux中多线程压缩软件 | Mingz

原文链接&#xff1a;Linux中多线程压缩软件 本期教程 软件网址&#xff1a; https://github.com/hewm2008/MingZ安装&#xff1a; git clone https://github.com/hewm2008/MingZ.git cd MingZ make cd bin ./mingz -h使用源码安装&#xff1a; 若是你的git无法使用安装&am…