初学 React hooks
umi: 3.5.40
React: 17.0.2
antd: 4.16.13
useState 和 useRef
使用小心得:
- setCount 之后不要直接使用 count ;
- 执行顺序要求比较严格的多用 useRef 会比较简洁。比如请求一个接口,获取响应值后马上带着响应值请求下一个接口,你可以用 let const useRef 去接这个响应值,但是千万别用 useState 去接,不然就要增加一个 useEffct ,否则你立马使用的肯定不是响应值。
- 能少用尽量少用,因为 useState 一旦改变状态会导致组件重新渲染。
const [count, setCount] = useState<number>(0); 参数一: state 声明的状态;参数二:更新参数一声明的状态的值。
- useState 会异步且批量更新数据,但是 useState 不能实时获取最新的值(React 更新状态数据之前多次执行 setCount ,取最后一次执行的 setCount);
- 基本数据类型的值发生改变或引用数据类型的引用地址发生改变时,再次使用 useState 的参数二才会再次执行;
- useState 的状态发生改变就会重新渲染组件;
- useEffect 通常和 useState 配合使用。(对于 useEffect 的副作用说法,我也不是很理解)
- useEffect(() => {}); 陷入死循环
- useEffect(() => {}, []); 挂载时执行一次
- useEffect(() => {}, [count]); 挂载(也可能是赋默认值,我没分清)时,执行一次;此后,只要 count
基本数据类型是值,引用数据类型是引用地址
发生改变时就会执行 useEffect 的回调函数,即参数一。(参数二放入 useRef , useRef 发生改变时不会执行 useEffect 的回调) 当 useEffect 没有监听到依赖的数据发生改变时存在两种可能(我已知),一是引用地址没有发生改变,二是 useEffect 放在子组件上监听父组件传来的消息,而子组件在父组件中没有满足条件不渲染也会导致 useEffect 监听不到依赖数据的变化。
const countRef = useRef<number>(0);
- countRef 可以实时获取最新的值;
- countRef 的改变不会引起组件的重新渲染;
- 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:初始加载
“123” “0” “false” 都是 TeachAssistantList.tsx 打印的,“456” “0 1” “false 1” 都是 FirstModal.tsx 打印的。
- 子组件先挂载;
- SecondModal.tsx 和 Watch.tsx 都没有打印说明不可见的 Modal 不会渲染 Modal 内的子组件。
图2:点击一次“改变 count 和 haha 的值”
“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 对话框)
“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 对话框的“确定”
“456” 是 FirstModal.tsx 打印的,"789"是 SecondModal.tsx 打印的,“watch SecondModal” “1 ‘SecondModal’” “true ‘SecondModal’” 都是 Watch.tsx 打印的。(此时的 count 为1, haha 为 true)
图6-1:点击两个 Modal 对话框的“取消”,回到最初的页面。
图6-2:点击两次“改变 count 和 haha 的值”
图6-3:先点击“显示弹窗”,弹出 FirstModal 对话框后点击 FirstModal 对话框的“确定”。
上面三张图都没有打印过"true ‘SecondModal’" 和 “false ‘SecondModal’”,说明不可见的 Modal 组件不会向子组件进行 props 传值,可见的 Modal 组件需要传递的值发生改变时才会向子组件进行 props 传值。