在react函数式组件开发中,useEffect是处理副作用的基石。但许多开发者仅停留在基础用法,未能充分发挥其价值。本文将深入剖析5个高频应用场景,同时纠正常见误解,助你写出更健壮的组件。
误区修正:useEffect回调并非在“挂载完成时”执行,而是在浏览器完成布局与绘制后(提交阶段)异步执行。
典型场景:
const ScrollTracker = () => {
useEffect(() => {
const handleScroll = () => console.log(window.scrollY);
// 事件绑定
window.addEventListener('scroll', handleScroll);
// 清理函数(组件卸载时执行)
return () => window.removeEventListener('scroll', handleScroll);
}, []); // 空依赖确保只运行一次
return <div>滚动监听器...</div>;
};
避坑指南:
原代码问题:initValue可能为undefined或null,直接初始化状态会导致后续更新失效
优化方案:
const FormInput = ({ initialValue }) => {
const [value, setValue] = useState(null);
const isInitialized = useRef(false);
useEffect(() => {
// 仅当获取到有效初始值且未初始化时更新
if (initialValue !== undefined && !isInitialized.current) {
isInitialized.current = true;
setValue(initialValue);
}
}, [initialValue]);
return <input value={value ?? ''} onChange={e => setValue(e.target.value)} />;
};
最佳实践:
场景需求:仅在特定状态变更时执行逻辑,忽略首次渲染
方案1:使用初始化标记
const PriceWatcher = ({ price }) => {
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
alert(`价格更新至:${price}`);
}, [price]);
};
方案2:自定义Hook封装(推荐)
function useUpdateEffect(callback, deps) {
const isFirst = useRef(true);
useEffect(() => {
if (isFirst.current) {
isFirst.current = false;
return;
}
return callback();
}, deps);
}
// 使用
useUpdateEffect(() => alert(`价格更新:${price}`), [price]);
重要原则:useEffect中可安全访问已提交的DOM
同步渲染场景:
const ElementMeasurer = () => {
const divRef = useRef(null);
useEffect(() => {
// 此处可获取真实DOM尺寸
console.log('元素宽度:', divRef.current.offsetWidth);
}, []);
return <div ref={divRef}>测量目标</div>;
};
异步数据渲染场景:
const DynamicList = () => {
const [items, setItems] = useState([]);
const itemRefs = useRef([]);
useEffect(() => {
fetch('/api/items').then(res => setItems(res.data));
}, []);
useEffect(() => {
if (items.length > 0) {
// 动态渲染后计算首个元素位置
const firstItemTop = itemRefs.current[0]?.getBoundingClientRect().top;
console.log('首项位置:', firstItemTop);
}
}, [items]); // 依赖项触发DOM更新后计算
return (
<div>
{items.map((item, index) => (
<div
key={item.id}
ref={el => itemRefs.current[index] = el}
>
{item.name}
</div>
))}
</div>
);
};
黄金法则:
示例:
const SearchBox = ({ fetchData }) => {
const [query, setQuery] = useState('');
// 正确:稳定函数引用
const throttledFetch = useCallback(throttle(fetchData, 500), [fetchData]);
useEffect(() => {
if (query) throttledFetch(query);
}, [query, throttledFetch]); // 安全依赖
return <input value={query} onChange={e => setQuery(e.target.value)} />;
};
无限循环陷阱
过时闭包问题
useEffect(() => {
const timer = setInterval(() => {
// 错误:始终读取初始state
console.log(count);
}, 1000);
return () => clearInterval(timer);
}, []); // 缺少count依赖
修复方案:
理解useEffect的核心在于建立渲染与副作用的关系:
通过合理运用依赖项控制、清理函数和初始化策略,可解决90%的React副作用管理需求。建议结合React DevTools的useEffect执行日志分析优化时机,构建高性能应用。
async/await 语法用看起来像写同步代码的方式来优雅地处理异步操作,但是我们也要明白一点,异步操作本来带有复杂性,像写同步代码的方式并不能降低本质上的复杂性,所以在处理上我们要更加谨慎, 稍有不慎就可能写出不是预期执行的代码,从而影响执行效率
$refs的使用场景:父组件调用子组件的方法,可以传递数据。$emit的使用:子组件调用父组件的方法并传递数据。$on的使用场景:兄弟组件之间相互传递数据
->用来引用一个类的属性(变量)、方法(函数);=>是用来定义数组用的;::用来直接调用类中的属性或方法,没有实例化;$this->表示实例化后调用具体对象
React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。还有一种新的短语法可用于声明它们。
作为一个前端开发,在浏览别人家的页面时总是会习惯性的查看他们页面的源码,发现大多数网站的页面,包括我自己写的页面中用到的最多的布局元素无外乎就是div、p、span、ul、dl、ol、li、dt、dd、strong、b
this是JavaScript的一个关键字,函数调用时才会出现;因为函数是在一定的环境中运行的,调用函数时肯定需要知道是[谁调用的]?就用到了this进行指向;this 既不指向函数自身,也不指函数的词法作用域,而是调用函数时的对象!
标签语义化就是让元素标签做适当的事情。例如 p 标签就是代表文本,button 标签代表按钮,nav 标签代表导航等等。其实标签语义化是给浏览器和搜索引擎看的。
try-catch属于同步代码块,因此无法捕获异步(重新开辟的线程,例如定时器,异步请求)代码中的异常,即能被try-catch捕获的异常,必须是在报错时候,线程的执行进入了try-catch代码块时,才能被捕获异常
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!