初学 React Hooks 和 antd-design

文章详细探讨了ReactHooks中的useState和useRef在处理状态和数据流时的差异,强调useState的异步性和批量更新特性,以及useRef对于实时获取最新值的优势。同时,分析了antd设计的Modal组件在不同状态下如何进行props传递,并指出不可见Modal不会渲染子组件及影响props传递。
摘要由CSDN通过智能技术生成

umi: 3.5.40
React: 17.0.2
antd: 4.16.13

useState 和 useRef

使用小心得:

  1. setCount 之后不要直接使用 count ;
  2. 执行顺序要求比较严格的多用 useRef 会比较简洁。比如请求一个接口,获取响应值后马上带着响应值请求下一个接口,你可以用 let const useRef 去接这个响应值,但是千万别用 useState 去接,不然就要增加一个 useEffct ,否则你立马使用的肯定不是响应值。
  3. 能少用尽量少用,因为 useState 一旦改变状态会导致组件重新渲染。

const [count, setCount] = useState<number>(0); 参数一: state 声明的状态;参数二:更新参数一声明的状态的值。

  1. useState 会异步且批量更新数据,但是 useState 不能实时获取最新的值(React 更新状态数据之前多次执行 setCount ,取最后一次执行的 setCount);
  2. 基本数据类型的值发生改变或引用数据类型的引用地址发生改变时,再次使用 useState 的参数二才会再次执行;
  3. useState 的状态发生改变就会重新渲染组件;
  4. useEffect 通常和 useState 配合使用。(对于 useEffect 的副作用说法,我也不是很理解)
    • useEffect(() => {}); 陷入死循环
    • useEffect(() => {}, []); 挂载时执行一次
    • useEffect(() => {}, [count]); 挂载(也可能是赋默认值,我没分清)时,执行一次;此后,只要 count 基本数据类型是值,引用数据类型是引用地址发生改变时就会执行 useEffect 的回调函数,即参数一。(参数二放入 useRef , useRef 发生改变时不会执行 useEffect 的回调)
    • 当 useEffect 没有监听到依赖的数据发生改变时存在两种可能(我已知),一是引用地址没有发生改变,二是 useEffect 放在子组件上监听父组件传来的消息,而子组件在父组件中没有满足条件不渲染也会导致 useEffect 监听不到依赖数据的变化。

const countRef = useRef<number>(0);

  1. countRef 可以实时获取最新的值;
  2. countRef 的改变不会引起组件的重新渲染;
  3. countRef 的改变不会触发 useEffect 的回调函数。
import { Button } from "antd";
import { memo, useEffect, useRef, useState } from "react";

const TeachAssistantList: React.FC = () => {
  const countRef = useRef<number>(0);
  const countRef2 = useRef<number>(0);
  const [count, setCount] = useState<number>(0);
  const [count2, setCount2] = useState<number>(0);
  const [count3, setCount3] = useState<number>(0);

  // 单纯更改 countRef2.current 不会执行 useEffect 的回调函数,但是 setCount(count + 1); 后就会再次执行
  useEffect(() => {
    console.log(countRef2.current, 'countRef2');
    setCount2(count2 + 1);
  }, [countRef2.current]); // 改为 countRef2 会导致连 countRef2.current 都监听不到

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

  // setInterval(() => {
  //   console.log(count3, 'count3');
  //   setCount3(count3 + 1);
  // }, 10000);

  return <div>
    {console.log(123)}
    <Button onClick={() => countRef.current += 1}>改变 countRef</Button>
    <Button onClick={() => countRef2.current += 1}>改变 countRef2</Button>
    <Button onClick={() => {
      // setCount(1);
      // setCount(2);
      // setCount(3);

      // setCount(count);

      setCount(count + 1);
    }}>改变 count</Button>
  </div>
};

export default memo(TeachAssistantList);

antd-design 的 Modal 组件的 props 传值

结论

        不可见的 Modal 组件不会向子组件进行 props 传值且子组件也不会渲染,但是它不会影响 return 之外的函数执行,也不会影响 return 内非子组件的函数执行(标签和组件都没渲染)。
        两个 Modal 之间存在一个显示另一个隐藏时,这两个 modal 之间是并列关系而不是嵌套关系。

TeachAssistantList.tsx

import { Button } from "antd";
import { memo, useEffect, useState } from "react";
import FirstModal from "./FirstModal";

const TeachAssistantList: React.FC = () => {
  // 显示弹窗
  const [firstShowModal, setFirstShowModal] = useState<boolean>(false);
  const [count, setCount] = useState<number>(0);
  const [haha, setHaha] = useState<boolean>(false);

  useEffect(() => {
    console.log(count);
  }, [count]);
  
  useEffect(() => {
    console.log(haha);
  }, [haha]);
  
  return <div>
    {console.log(123)}
    <Button onClick={() => {
      setFirstShowModal(true);
    }}>显示弹窗</Button>
    <Button onClick={() => {
      setCount(count + 1);
      setHaha(!haha);
    }}>改变 count 和 haha 的值</Button>
    <FirstModal
      firstShowModal={firstShowModal}
      setFirstShowModal={setFirstShowModal}
      count={count}
      setCount={setCount}
      haha={haha}
      setHaha={setHaha}
    />
  </div>
};
export default memo(TeachAssistantList);

FirstModal.tsx

import { Button, Modal } from "antd";
import { memo, useEffect, useState } from "react";
import Watch from "../Watch";
import SecondModal from "../SecondModal";

interface PropsType {
  firstShowModal?: boolean;
  setFirstShowModal?: (firstShowModal: boolean) => void;
  count?: number;
  setCount?: (count: number) => void;
  haha?: boolean;
  setHaha?: (haha: boolean) => void;
}

const FirstModal: React.FC<PropsType> = ({
  firstShowModal = false,
  setFirstShowModal,
  count = 0,
  setCount,
  haha = false,
  setHaha
}) => {
  const [secondShowModal, setSecondShowModal] = useState<boolean>(false);

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

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

  return <Modal
    centered
    maskClosable={false}
    visible={firstShowModal}
    onCancel={() => setFirstShowModal?.(false)}
    onOk={() => setSecondShowModal(true)}
  >
    {console.log(456)}
    <Button onClick={() => {
      setCount?.(count + 1);
      setHaha?.(!haha);
    }}>改变 count 和 haha 的值</Button>
    <Watch
      type="FirstModal"
      count={count}
      haha={haha}
    />
    <SecondModal
      secondShowModal={secondShowModal}
      setSecondShowModal={setSecondShowModal}
      count={count}
      haha={haha}
    />
  </Modal>
}

export default memo(FirstModal);

{console.log(456)} 用 div 标签包裹,打印照样执行,如果没显示过 FirstModal 对话框并不会有 div 标签。
SecondModal.tsx

import { Button, Modal } from "antd";
import { memo, useEffect } from "react";
import Watch from "../Watch";

interface PropsType {
  secondShowModal?: boolean;
  setSecondShowModal?: (secondShowModal: boolean) => void;
  count?: number;
  haha?: boolean;
}

const SecondModal: React.FC<PropsType> = ({
  secondShowModal = false,
  setSecondShowModal,
  count = 0,
  haha = false
}) => {
  useEffect(() => {
    console.log(count, 2);
  }, [count]);

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

  return <Modal
    centered
    visible={secondShowModal}
    onCancel={() => setSecondShowModal?.(false)}
    onOk={() => setSecondShowModal?.(false)}
  >
    {console.log(789)}
    <Watch
      type="SecondModal"
      count={count}
      haha={haha}
    />
  </Modal>
}

export default memo(SecondModal);

Watch.tsx

import { memo, useEffect } from "react";

interface PropsType {
  type?: string;
  count?: number;
  setCount?: (count: number) => void;
  haha?: boolean;
  setHaha?: (haha: boolean) => void;
}

const Watch: React.FC<PropsType> = ({
  type = 'normal',
  count = 0,
  haha = false
}) => {
  useEffect(() => {
    console.log(count, type);
  }, [count]);

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

  return <div>
    {console.log('watch', type)}
    <div>{ count }, { type }</div>
    <div>{ haha }, { type }</div>
  </div>
}

export default memo(Watch);

截图分析

图1:初始加载
antd-design 的 Modal 组件

“123” “0” “false” 都是 TeachAssistantList.tsx 打印的,“456” “0 1” “false 1” 都是 FirstModal.tsx 打印的。

  1. 子组件先挂载;
  2. SecondModal.tsx 和 Watch.tsx 都没有打印说明不可见的 Modal 不会渲染 Modal 内的子组件。

图2:点击一次“改变 count 和 haha 的值”
antd-design 的 Modal 组件

“123” “1” “true” 都是 TeachAssistantList.tsx 打印的,“456” “1 1” “true 1” 都是 FirstModal.tsx 打印的。

        说明 TeachAssistantList.tsx 的 count 和 haha 成功通过 props 传递给了 FirstModal.tsx 并且使 FirstModal.tsx 发生了重新渲染,此外, Modal 组件内的子组件没有挂载。


图3:点击一次“显示弹窗”(显示 FirstModal 对话框,不显示 SecondModal 对话框)
antd-design 的 Modal 组件

“123” 是 TeachAssistantList.tsx 打印的,“456” 是 FirstModal.tsx 打印的,“789” “1 2” “true 2” 都是 SecondModal.tsx 打印的,“watch FirstModal” “1 ‘FirstModal’” “true ‘FirstModal’” 都是 Watch.tsx 打印的。(此时的 count 为1, haha 为 true)

        因为没有打印 “watch SecondModal” “1 ‘SecondModal’” “true ‘SecondModal’”,所以不可见的 SecondModal 和不可见的 FirstModal 效果是一样的,说明 Modal 里面套 Modal 只需要考虑一个 Modal 的情况。


图5:点击 FirstModal 对话框的“确定”
antd-design 的 Modal 组件

“456” 是 FirstModal.tsx 打印的,"789"是 SecondModal.tsx 打印的,“watch SecondModal” “1 ‘SecondModal’” “true ‘SecondModal’” 都是 Watch.tsx 打印的。(此时的 count 为1, haha 为 true)


图6-1:点击两个 Modal 对话框的“取消”,回到最初的页面。
antd-design 的 Modal 组件
图6-2:点击两次“改变 count 和 haha 的值”
antd-design 的 Modal 组件
图6-3:先点击“显示弹窗”,弹出 FirstModal 对话框后点击 FirstModal 对话框的“确定”。
antd-design 的 Modal 组件
        上面三张图都没有打印过"true ‘SecondModal’" 和 “false ‘SecondModal’”,说明不可见的 Modal 组件不会向子组件进行 props 传值,可见的 Modal 组件需要传递的值发生改变时才会向子组件进行 props 传值。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值