React---day6、7

6、组件之间进行数据传递

**6.1 父传子:**props传递属性

父组件:

复制代码
 <div>
              <ChildCpn name="蒋乙菥" age="18" height="1,88" />
</div>

子组件:

复制代码
export class ChildCpn extends React.Component{
    render(){
        const {name , age , height} = this.props
       return (
         <div>
            <h2>{"姓名" + name + "," + "年龄" + age + "身高" + height}</h2>
        </div>
       )
    }
    
}

**6.2 子传父:**父组件定义函数,子组件调用函数

父组件:

复制代码
  <BtnCpn addNum={this.addNum.bind(this)}/>
     addNum() {
        this.setState({
            num : this.state.num + 1
        })
    }

子组件:

复制代码
import React from "react";

export class BtnCpn extends React.Component{
    render(){
        const { addNum } = this.props
        return (
            <div onClick={addNum}>
                +1
            </div>
        )
    }
}

但是我们也出现了this的绑定问题:

复制代码
        子传父通信.js:22 
 Uncaught TypeError: Cannot read properties of undefined (reading 'setState')
    at addNum (子传父通信.js:22:1)

nind绑定

复制代码
     <BtnCpn addNum={this.addNum.bind(this)}/>

箭头函数:

复制代码
      addNumTwo = () =>  {
        this.setState({
            num : this.state.num + 1
        })//箭头函数
    }

6.3 阶段案例

由两部分组成:tabBar以及父组件

父组件传递数据给子组件,点击子组件,传递数据给父组件,切换内容

App.js

复制代码
import React from "react";
import { TabBar } from "./tabBar";
export class App extends React.Component{
       constructor(){
        super();
        this.state =  {
            tabList : ["流行" , "新款" , "精选"],
            context : "流行",
            curIndex: 0
        }
    }
       render(){
        return (
          <div className="content">
              <TabBar 
              clickItem={this.handleTabClick}
              curIndex={this.state.curIndex}
              tabList={this.state.tabList} />

              <h2>{this.state.context}</h2>
          </div>
        )
        
       }
           handleTabClick = (index) => {
        this.setState({
            curIndex: index,
            context: this.state.tabList[index]
        });//更改的函数
    }

  
}

tabBar

复制代码
import React from "react";
export class TabBar extends React.Component{
    render(){
        const {tabList  , curIndex , clickItem } = this.props;
        return (
            <ul className="tab">
                {
                    tabList.map((item, index) => (
                        <li
                            key={index}
                            className={`item${curIndex === index ? ' active' : ''}`}
                            onClick={() => clickItem(index)}
                        >{item}</li>
                    ))
                }
            </ul>
        )
    }
}

6.4 跨组件通信

context相关的api

  1. React.createContext

用于创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 的组件时,它会从组件树中最近的 Provider 中读取当前的 context 值。

  1. Context.Provider

每个 Context 对象都会有一个 Provider React 组件,它允许消费组件订阅 context 的变化。

  1. Class.contextType

用于 class 组件中订阅 Context,只有当组件中使用了 contextType,组件才会订阅 Context 的变化。

  1. Context.Consumer

用于函数式组件中订阅 Context。它允许你订阅 context 的变化,并在 context 发生变化时重新渲染组件。

  1. useContext Hook

用于函数式组件中订阅 Context。它接收一个 context 对象(从 React.createContext 创建)并返回该 context 的当前值。

实例:

复制代码
import React, { createContext, useContext } from 'react';

// 创建一个 Context 对象
const ThemeContext = createContext('light');

// 一个函数式组件,使用 useContext 订阅 Context
function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme === 'dark' ? 'black' : 'white', color: theme === 'dark' ? 'white' : 'black' }}>
    I am styled by theme!
  </button>;
}

// 一个中间组件
function Toolbar() {
  return (
    <ThemedButton />
  );
}

// 应用程序的顶层组件
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

export default App;

7、slot插槽

其实也算是传参数叭,只是参数是html表达式

App.js

复制代码
      <NavBar2 leftSlot={<span>aaa</span>} 
               centerSlot={<span>bbb</span>}
               rightSlot={<span>ccc</span>}
               />

NavBar2:

复制代码
export class NavBar2 extends React.Component {
  render() {
    const {leftSlot , centerSlot , rightSlot } = this.props;
    return (
        <div className="main">
      <div className="bay-left">{leftSlot}</div>
      <div className="bay-mid">{centerSlot}</div>
      <div className="bay-right">{rightSlot}</div>
    </div>
    )
  }
}

8、setState

8.1 为什么要使用setState?

  • 开发中我们并不能直接通过修改state的值来让界面发生更新:
  • 因为我们修改了state之后,希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生了变化;
  • React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化;
  • 我们必须通过setState来告知React数据已经发生了变化;

8.2 为什么可以直接this.setState

setState方法是从Component中继承过来的。

8.3 为什么setState是异步的

  • setState设计为异步,可以显著的提升性能;
    • 如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的;
    • 最好的办法应该是获取到多个更新,之后进行批量更新;
  • 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步;
    • state和props不能保持一致性,会在开发中产生很多的问题;

8.4 setState一定是异步的吗

  • 在组件生命周期或React合成事件中,setState是异步;
  • 在setTimeout或者原生dom事件中,setState是同步

8.5 数据的合并

当this.state里面有两个属性:name、title

我们修改只修改name,那么title会不会受到影响呢?答案是不会

源码中其实是有对 原对象 和 新对象进行合并的:

复制代码
Object.assign

setState可以传入参数或者是函数:

传入参数:数据会进行合并,多个setSEtate合并更新为1个

传入函数:数据不会进行合并

最后会+3

9、React的更新流程

更新优化的方法:

9.1 对比不同类型的元素

当节点为不同的元素,React会拆卸原有的树,并且建立起新的树:

  • 当一个元素从 变成 ,从 变成 ,或从 变成
    都会触发一个完整的重建流程;
  • 当卸载一棵树时,对应的DOM节点也会被销毁,组件实例将执行 componentWillUnmount() 方法;
  • 当建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中,组件实例将执行 componentWillMount() 方法,紧接着 componentDidMount() 方法;

9.2 对比同一类型的元素

当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性。

9.3 对子节点进行递归

在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个

mutation。

优化:加入一个key属性

当子元素(这里的li)拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素:

在下面这种场景下,key为111和222的元素仅仅进行位移,不需要进行任何的修改;

将key为333的元素插入到最前面的位置即可;

key的注意事项:

  • key应该是唯一的;
  • key不要使用随机数(随机数在下一次render时,会重新生成一个数字);
  • 使用index作为key,对性能是没有优化的(key无法匹配,还是会产生mutation)

9.4 shouldComponentUpdate

复制代码
import React from 'react';
import './App.css';

class App extends React.Component {
 constructor(){
  super()
  this.state = {
    counter:0,
    message :"你好呀"
  }
 }
 render(){
  console.log("render正在被调用")
  return (
    <div>
      <h2>当前计数为:{this.state.counter}</h2>
      <button onClick={() => this.btnAdd()}>+1</button>
    </div>
  )

 }
 btnAdd(){
  this.setState({
        counter:this.state.counter+1
  })
 }
}

export default App;

每一次点击按钮都会重新调用render渲染,这是非常消耗性能的

我们可以使用shouldComponentUpdate提升性能

  • 该方法有两个参数:

    • 参数一:nextProps 修改之后,最新的props属性
    • 参数二:nextState 修改之后,最新的state属性
  • 该方法返回值是一个boolean类型

    • 返回值为true,那么就需要调用render方法;

    • 返回值为false,那么久不需要调用render方法;

    • 默认返回的是true,也就是只要state发生改变,就会调用render方法;

      import React from 'react';
      import './App.css';

      class App extends React.Component {
      constructor(){
      super()
      this.state = {
      counter:0,
      message :"你好呀"
      }
      }
      shouldComponentUpdate(nextProps , nextState){
      if(this.state.counter !== nextState.counter){
      return true;

      }
      return false;

      }
      render(){
      console.log("render正在被调用")
      return (


      当前计数为:{this.state.counter}


      <button onClick={() => this.btnAdd()}>+1</button>

      {this.state.message}


      <button onClick={() => this.changeText()}>改变文本</button>

      复制代码
      </div>

      )

      }
      btnAdd(){
      this.setState({
      counter:this.state.counter+1
      })
      }
      changeText(){
      this.setState({
      message:"蒋乙菥"
      })
      }
      }

      export default App;

  • 如果所有的类,我们都需要手动来实现 shouldComponentUpdate,那么会给我们开发者增加非常多的工作量。

    • 我们来设想一下shouldComponentUpdate中的各种判断的目的是什么?
    • props或者state中的数据是否发生了改变,来决定shouldComponentUpdate返回true或者false;
  • 事实上React已经考虑到了这一点,所以React已经默认帮我们实现好了,如何实现呢?

    • 将class继承自PureComponent(比较对象是否相等,依赖stte会发生改变)。

      import React from 'react';
      import './App.css';

      class Pure extends React.PureComponent {
      constructor(){
      super();
      this.state = {
      counter: 0,
      message: "你好呀"
      }
      }

      btnAdd = () => {
      this.setState({
      counter: this.state.counter + 1
      })
      }

      changeText = () => {
      this.setState({
      message: "蒋乙菥"
      })
      }

      render(){
      console.log("render正在被调用")
      return (


      当前计数为:{this.state.counter}


      <button onClick={this.btnAdd}>+1</button>

      {this.state.message}


      <button onClick={this.changeText}>改变文本</button>

      )
      }
      }

      export default Pure;

9.5 memo

对函数组件做优化:在有改变的时候才进行渲染

相关推荐
yuren_xia1 小时前
Spring Boot中保存前端上传的图片
前端·spring boot·后端
普通网友2 小时前
Web前端常用面试题,九年程序人生 工作总结,Web开发必看
前端·程序人生·职场和发展
站在风口的猪11084 小时前
《前端面试题:CSS对浏览器兼容性》
前端·css·html·css3·html5
JohnYan4 小时前
Bun技术评估 - 04 HTTP Client
javascript·后端·bun
青莳吖5 小时前
使用 SseEmitter 实现 Spring Boot 后端的流式传输和前端的数据接收
前端·spring boot·后端
CodeCraft Studio6 小时前
PDF处理控件Aspose.PDF教程:在 C# 中更改 PDF 页面大小
前端·pdf·c#
拉不动的猪6 小时前
TS常规面试题1
前端·javascript·面试
再学一点就睡6 小时前
实用为王!前端日常工具清单(调试 / 开发 / 协作工具全梳理)
前端·资讯·如何当个好爸爸
穗余7 小时前
NodeJS全栈开发面试题讲解——P5前端能力(React/Vue + API调用)
javascript·vue.js·react.js
Jadon_z7 小时前
vue2 项目中 npm run dev 运行98% after emitting CopyPlugin 卡死
前端·npm