• 【react】基础知识补充及原理【粗略版】


    1.补充知识

    1.PureComponent和React.Component的区别

    PureComonent默认给类组件添加了一个shouldComponentUpdate的钩子函数,在这个钩子中,会对新旧状态及属性做一个浅比较,以此达到优化组件渲染的目的。

    2.基于ref获取DOM元素的语法

    1.给原生元素设置ref

    给需要获取dom的元素设置ref='xxx',然后使用this.refs.xxx进行获取,不建议使用,在React.StrictMode模式下会报错

    1. render() {
    2. return <div>
    3. <h2 className="title" ref="titleBox">温馨提示h2>
    4. div>;
    5. }
    6. componentDidMount() {
    7. console.log(this.refs.titleBox);
    8. }

    将ref设置为一个函数,并将ref的形参(dom元素)挂载到实例上

    1. render() {
    2. return <div>
    3. <h2 className="title" ref={x => this.box2 = x}>友情提示h2>
    4. div>;
    5. }
    6. componentDidMount() {
    7. console.log(this.box2);
    8. }

    基于React.createRef()创建一个ref对象,初始化时为null 

    1. box3 = React.createRef();
    2. render() {
    3. return <div>
    4. <h2 className="title" ref={this.box3}>郑重提示h2>
    5. div>;
    6. }
    7. componentDidMount() {
    8. console.log(this.box3.current);
    9. }

    2.给组件设置ref

    类组件:获取当前组件的实例,通常用于父子组件传值及方法调用

    1. class Demo extends React.Component {
    2. render() {
    3. return <div>
    4. //子组件
    5. <Child1 ref={x => this.child1 = x} />
    6. div>;
    7. }
    8. componentDidMount() {
    9. console.log(this.child1);
    10. }
    11. }

     函数组件:获取函数组件内部某个元素。需要使用React.forwardRef()包裹子组件,这是函数子组件将拥有除props外的另一个形参:ref。通过把该形参设置给函数子组件中的某个元素,来达到获取函数子组件中dom元素的目的。

    1. import React from "react";
    2. const Child2 = React.forwardRef(function Child2(props, ref) {
    3. // 该ref形参为给子组件设置的ref值: x => this.child2 = x
    4. return <div>
    5. 子组件2
    6. <button ref={ref}>按钮button>
    7. div>;
    8. });
    9. class Demo extends React.Component {
    10. render() {
    11. return <div>
    12. <Child2 ref={x => this.child2 = x} />
    13. div>;
    14. }
    15. componentDidMount() {
    16. console.log(this.child2); //子组件内部的button按钮
    17. }
    18. }

    3.React中的插槽

    基于子组件中props.children获取传递的插槽信息(子节点信息)。

      + 调用组件时,基于双闭合调用方式把插槽信息(子节点信息)传递给组件,组件内部进行渲       染

    1. const DemoOne = function Demo(props) {
    2. let { title, children } = props;
    3. return <div>
    4. <h2>{title}h2>
    5. <br/>
    6. {children}
    7. div>;
    8. };
    9. //传递时
    10. <Demo title="demo">
    11. //编译为vdom后作为props.children的值
    12. <div>我是插槽内容div>
    13. Demo>

    具名插槽

    1. import React from 'react';
    2. const DemoOne = function Demo(props) {
    3. let { title, children } = props;
    4. // 对children的类型做处理
    5. // 可以基于 React.Children 对象中提供的方法,对props.children做处理:
    6. count\forEach\map\toArray... 在这些方法的内部,已经对children的各种形式做了处理
    7. children = React.Children.toArray(children);
    8. let headerSlot = [],
    9. footerSlot = [],
    10. defaultSlot = [];
    11. children.forEach(child => {
    12. // 传递进来的插槽信息,都是编译为virtualDOM后传递进来的,而不是传递的标签
    13. let { slot } = child.props;
    14. if (slot === 'header') {
    15. headerSlot.push(child);
    16. } else if (slot === 'footer') {
    17. footerSlot.push(child);
    18. } else {
    19. defaultSlot.push(child);
    20. }
    21. });
    22. return <div>
    23. {headerSlot}
    24. <br />
    25. <h2>{title}h2>
    26. <br />
    27. {footerSlot}
    28. div>;
    29. };
    30. <Demo title="学插槽">
    31. <span slot="footer">我是页脚span>
    32. <span>我是匿名的span>
    33. <span slot="header">我是页眉span>
    34. Demo>

    4.setState(partialState, callback)

    • callback 发生在 componentDidUpdate 周期函数之后
    • 特殊点:如果基于shouldComponentUpdate阻止了视图更新,componentDidUpdate钩子不会执行,但是callback会执行

    tips:类似于Vue的$nextTick

    5.setState的异步(react18)

    在react18中,setState是异步的,无论是合成事件,周期函数,定时器等等。这时setState实现对状态的批处理,即将需更新的状态放入更新队列【updater】中进行处理。批处理有效减少更新次数,降低性能消耗

    在这段代码中,三句setState代码不会立即更新状态及视图,代码顺次执行,遇到setState则将其加入更新队列中,最后让更新队列中的任务统一更新/渲染一次(批处理)。故上述代码render只执行一次,而三个console.log也只是打印更新之前的值。 

    1. handle = () => {
    2. let { x, y, z } = this.state;
    3. this.setState({ x: x + 1 });
    4. console.log(this.state.x);
    5. this.setState({ y: y + 1 });
    6. console.log(this.state.y);
    7. this.setState({ z: z + 1 });
    8. console.log(this.state.z);
    9. };
    10. render() {
    11. console.log('我是render');
    12. ...
    13. }

    tips:批处理的机制是微任务吗?如果不是,那么和微任务有什么区别?或者说是一种类异步操作

    在定时器中时,批处理将如何进行?

    这里将不等待setTimeout执行,而是先将setTimeout之外的两个setState先放入更新队列批处理渲染一次;然后1000ms后setTimeout执行,再将定时器内部的setState放入更新队列中批处理渲染

    1. handle = () => {
    2. let { x, y, z } = this.state;
    3. this.setState({ x: x + 1 });
    4. this.setState({ y: y + 1 });
    5. console.log(this.state);
    6. setTimeout(() => {
    7. this.setState({ z: z + 1 });
    8. console.log(this.state);
    9. }, 1000);
    10. };
    11. render() {
    12. console.log('我是render');
    13. ...
    14. }

    tips:如果setTimeout不传入时间呢?

    在多个时间差距不大或者时间差距极小的定时器中,分别有setState,那么批处理如何进行

    在当前相同时间段内【浏览器此时可以处理的事情中】,遇到setState会立即放入更新队列

    1. handle = () => {
    2. let { x, y, z } = this.state;
    3. setTimeout(() => {
    4. this.setState({ x: x + 1 });
    5. console.log(this.state);
    6. }, 1000);
    7. setTimeout(() => {
    8. this.setState({ y: y + 1 });
    9. console.log(this.state);
    10. }, 1000);
    11. setTimeout(() => {
    12. this.setState({ z: z + 1 });
    13. console.log(this.state);
    14. }, 1000);
    15. };
    16. render() {
    17. console.log('我是render');
    18. ...
    19. }

    上述代码中,由于setTimeout同时执行,时间差距极小,故setState批处理渲染一次。那么在不断更改三个setTimeout的时间中,时间差距大于2ms,就有可能被分开批处理。

    6.setState的同步(react18以下)

     react18以下,在合成事件【jsx元素中基于onXxx绑定的事件】,周期函数中,setState的操作是异步的;而在定时器,手动获取DOM元素事件绑定【元素.addEventListener()】等,它将变为同步操作(立即更新状态)

    1. handle = () => {
    2. let { x, y, z } = this.state;
    3. this.setState({ x: x + 1 });
    4. this.setState({ y: y + 1 });
    5. console.log(this.state);
    6. setTimeout(() => {
    7. this.setState({ z: z + 1 });
    8. console.log(this.state);
    9. }, 1000);
    10. };
    11. render() {
    12. console.log('我是render');
    13. ...
    14. }

    该段代码先执行打印【我是render】(此结果由setTimeout外的两个setState批处理执行得到);1s后,打印z为处理后的值,即说明定时器执行时,setState为同步,此时console.log能打印最新的z值。

    7.setState的第一个参数为函数时 

    setState((prevState) => { //prevState: 存储之前的状态值

       return {

          xxx: xxx

       }

    })

    多次修改同一个state值时,可以通过该方式获取上一轮的state值

    1. handle = () => {
    2. //x为0
    3. for(let i=0;i<20;i++){
    4. //a代码
    5. this.setState({x: x+1});
    6. //b代码
    7. this.setState(prevState => {
    8. return {
    9. x: prevState + 1
    10. }
    11. });
    12. }
    13. };
    14. render() {
    15. console.log('我是render');
    16. ...
    17. }

    上述a代码由于批处理机制的原因会打印1;而b代码则是将setState的第一个参数:函数放入更新队列,把函数依次执行,最终得到叠加后的x值,进行一次渲染。

    8.flushSync

     flushSync 可以刷新【updater】更新队列,即让修改状态的任务立即批处理一次。

    这是一个实验性的api,不利于性能,谨慎使用。

    1. import { flushSync } from 'react-dom';
    2. handle = () => {
    3. let { x, y, z } = this.state;
    4. this.setState({ x: x + 1 });
    5. console.log(this.state.x);
    6. //此处去刷新队列,会得到最新的x和y
    7. flushSync(() => {
    8. this.setState({ y: y + 1 });
    9. console.log(this.state.y);
    10. });
    11. //此时的z会是最新的x值和y值之和
    12. this.setState({ z: z + 1 });
    13. console.log(this.state.z);
    14. };
    15. render() {
    16. console.log('我是render');
    17. ...
    18. }

     

     

  • 相关阅读:
    sent2vec教程
    stable diffusion API接口 + 扩展接口
    【SQL】指定日期的产品价格(IFNULL函数)
    java项目_第166期ssm多人命题系统_java毕业设计_计算机毕业设计
    LeetCode 515 Find Largest Value in Each Tree Row
    涨薪跳槽利器,清华大咖总结的 Java 核心突击讲,一应俱全
    【ONE·C++ || 网络基础(二)】
    字符串函数用指针定义
    转投go系列-谈谈profile工具1
    SOLID之DIP-依赖反转原则
  • 原文地址:https://blog.csdn.net/THcoding_Cat/article/details/128166232