react官网查缺补漏

89 阅读4分钟

心血来潮逛了一遍react官网文档,按照文档目录对之前没有深入了解的知识点进行了归纳,可以配合react官网文档进行阅读

react渲染执行顺序

useEffect

  • 执行时机:React 完成 DOM 更新后且浏览器绘制结束后,异步调用。不阻塞渲染
  • 执行顺序:
    • 挂载时会执行一次副作用(传入的函数)(注:开发环境严格模式下会先执行一次副作用与清理函数的循环(做一次检测))
    • 更新时会先执行上一次状态的清理函数,再以最新状态执行副作用
  • 用途:适用于不需要阻塞浏览器绘制的副作用,如网络请求、日志记录、设置订阅等。

useLayoutEffect

  • 执行时机:在 React 完成 DOM 更新后浏览器绘制之前,同步调用。阻塞渲染
  • 用途:适用于需要读取 DOM 布局并同步重渲染的副作用,如测量 DOM 元素尺寸、同步布局等。因为它会在浏览器有机会绘制之前执行,所以可能会影响性能。

打印顺序:1,2,6,10,9,8,7,5,3,4

  1. 父函数作用于->子函数作用域->子effect->父effect
  2. effect 按照定义顺序执行
import "./styles.css";
import { memo, useCallback, useEffect, useMemo } from "react";

import React, { useState } from "react";
import { Button, Spin } from "antd";


// 父组件
const App = () => {
  console.log(1);
  const [loading, setLoading] = useState(false);
  const [count, setCount] = useState(0);
  const conutObj = useMemo(() => {
    a: 1;
  }, []);
  console.log(2);
  useEffect(() => {
    console.log(5);
  }, []);

  useEffect(() => {
    console.log(3);
  });
  useEffect(() => {
    console.log(4);
  });

  const handleClick = useCallback(() => {
    console.log(count);

    setCount((pre) => pre + 1);
    console.log("click");
  }, []);
  const handleClickLoad = () => {
    setLoading(!loading);
  };
  console.log("父渲染了");
  return (
    <div>
      {loading ? "loading" : "load"}

      <Button onClick={handleClickLoad}>修改loading</Button>
      <Button onClick={handleClick}>修改count</Button>
      <Child
        onClick={handleClick}
        count={count}
        setCount={setCount}
        conutObj={conutObj}
        // setConutObj={setConutObj}
      />
    </div>
  );
};
// 子组件
const Child = memo((props) => {
  console.log(6);
  useEffect(() => {
    console.log(9);
  });

  useEffect(() => {
    console.log(8);
  }, []);

  useEffect(() => {
    console.log(7);
  }, [props.count]);
  console.log(10);

  // console.log(props.count);
  console.log("子组建渲染了");
  return <Button onClick={props.onClick}>child</Button>;
});

export default App;

state 如同一张快照

React 会使 state 的值始终“固定”在一次渲染的各个事件处理函数内部

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setTimeout(() => {
          alert(number);
        }, 3000);
      }}>+5</button>
    </>
  )
}

以上例子中,即使加了5s的定时器,但是每次渲染的值都是相同的,即0,5,10

把一系列 state 更新加入队列

想在重新渲染之前读取最新的state,可以传入纯函数,参数为最新的state值

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
      }}>增加数字</button>
    </>
  )
}

在下一次渲染期间,React 会遍历 state 队列:

更新队列n返回值
“替换为 50(未使用)5
n => n + 155 + 1 = 6

React 会保存 6 为最终结果并从 useState 中返回。

更新state中的数组

当你操作 React state 中的数组时,你需要避免使用(改变数组)的左列的方法,而首选右列的(返回新数组)方法

| 避免使用 (会改变原始数组) | 推荐使用 (会返回一个新数组) | | --------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------- | | 添加元素 | pushunshift | concat[...arr] 展开语法(例子) | | 删除元素 | popshiftsplice | filterslice例子) | | 替换元素 | splicearr[i] = ... 赋值 | map例子) | | 排序 | reversesort | 先将数组复制一份(例子

受控组件和非受控组件

当编写一个组件时,你应该考虑哪些信息应该受控制(通过 props控制),哪些信息不应该受控制(通过自身 state 控制)。当然,你可以随时改变主意并重构代码。

对 state 进行保留和重置

  • 只要在相同位置渲染的是相同组件, React 就会保留状态。
// 因为渲染在ui树的同个位置,即使切换组建状态也会被保留
import { useState } from 'react';

export default function App() {
  const [isFancy, setIsFancy] = useState(false);
  return (
    <div>
      {isFancy ? (
        <Counter isFancy={true} /> 
      ) : (
        <Counter isFancy={false} /> 
      )}
      <label>
        <input
          type="checkbox"
          checked={isFancy}
          onChange={e => {
            setIsFancy(e.target.checked)
          }}
        />
        使用好看的样式
      </label>
    </div>
  );
}

  • state 不会被保存在 JSX 标签里。它与你在树中放置该 JSX 的位置相关联。
  • 你可以通过为一个子树指定一个不同的 key 来重置它的 state。
  • 不要嵌套组件的定义,否则你会意外地导致 state 被重置。

flushSync 立即更新dom

要强制 React 同步更新(“刷新”)DOM。 为此,从 react-dom 导入 flushSync将 state 更新包裹flushSync 调用中:

import { flushSync } from 'react-dom';

flushSync(() => {
  // 执行完立刻更新dom
  setTodos([ ...todos, newTodo]);

});

// 能滚动到变化后最新的dom上
listRef.current.lastChild.scrollIntoView();

这将指示 React 当封装在 flushSync 中的代码执行后,立即同步更新 DOM。因此,当你尝试滚动到最后一个待办事项时,它已经在 DOM 中了

useEffectEvent - react18新hook

useEffectEvent 这个 React Hook 让你可以提取非响应式逻辑到 Effect Event 中。

  • 可以在useEffect 中执行响应式的逻辑且不需要监听这个响应式的变量
  • 声明后必须在useEffect中调用这个方法
const onSomething = useEffectEvent(callback)

最后:如果这篇文章如果帮到你了,帮忙点个赞,或者关注哈!!!