一、问题描述
1.1 触发现象
点击按钮后页面卡死
1.2 最小 Demo
- CodeSandBox:https://codesandbox.io/p/sandbox/react-hook-component-stuck-755wcy
- inscode:https://inscode.csdn.net/
import './App.css';
import React, { useState, useEffect } from "react";
const Demo = ({ value = [] }) => {
const [state, setState] = useState();
useEffect(() => {
console.log("value", value);
setState((value || []).filter((item) => item !== ""));
}, [value]);
return <div>list:{state}</div>;
};
export default function App() {
const [list, setList] = useState(["1", "2"]);
return (
<div className="App">
<Demo value={list} />
<h1 onClick={() => setList([...list, "a"])}>Add List</h1>
<h1 onClick={() => setList(undefined)}>Clear List</h1>
</div>
);
}
二、原因分析
2.1 排查过程
2.1.1 console 输出查看
没有报错日志
2.1.2 performance 查看
setState 方法耗时较长
2.1.3 源码屏蔽分析
二分法屏蔽问题代码并大致定位范围为 Demo 组件引起
2.1.4 源码加 log 分析
第 7 行加 log 发现,Value 入参传为 undefined 时会循环打印 log
2.2 原因分析
- 入参默认空数组不合理,组件内部更新状态都会拿到一个全新的入参空数组
const Demo = ({ value = [] }) => {
- 状态更新不合理,依赖入参状态并处理后再显示,还使用了空数组兜底,这里也没有判断入参 Value 本身为空的情况
useEffect(() => {
console.log("value", value);
setState((value || []).filter((item) => item !== ""));
}, [value]);
三、后续预防
3.1 入参默认值
使用 useEffect 监听的入参尽量不给默认值,并且处理好入参的各种边界情况
3.2 入参与视图
简单的入参处理尽量省略,可以直接使用入参做视图展示,比如:
return <div>list:{value?.length > 0 && value.filter(Boolean)}</div>;