我的网站开发技术经验总结 我的网站开发技术经验总结
首页

fangdown

我的网站开发技术经验总结
首页
  • 日记
  • 22
  • 05
fangdown
2022-05-13
目录

useEvent子组件性能的解决方案

# useEvent子组件性能的解决方案

# 背景

大多数人都有过表单的开发经验, 在现在的组件结构中往往是这样的

  • 表单父组件
    • 表单项组件
    • 表单项组件
    • 表单项组件
    • 表单项组件
    • 表单项组件
    • 表单项组件
  1. 在父组件中设定表单初始值formData, 并负责回调函数的相关处理
  2. 往子组件传需要的表单值和回调方法

那么经常会遇到更改了某一个表单项,会触发整个表单的的所有组件、子组件的渲染,特别影响性能!!!

const High = React.memo((props: any) => {
    const { onClick1, high } = props;
    console.log('render high');
    return (
        <>
            身高: {high}
            <Button onClick={onClick1}>身高+1</Button>
        </>
    );
});
const Age = React.memo((props: any) => {
    const { onClick2, age } = props;
    console.log('render age');
    return (
        <>
            年龄: {age}
            <Button onClick={onClick2}>年龄+1</Button>
        </>
    );
});
const Chat = () => {
    const [formData, setFormData] = useState({
        high: 0,
        age: 0,
    });
    const onClick1 = useCallback(() => {
        setFormData({ ...formData, high: formData.high + 1 });
    }, [formData]);
    const onClick2 = useCallback(() => {
        setFormData({ ...formData, age: formData.age + 1 });
    }, [formData]);
    return (
        <div>
            <High onClick1={onClick1} high={formData.high} />
            <Age onClick2={onClick2} age={formData.age} />
        </div>
    );
};
  • 点击high或者age组件都会引起对方的重新渲染

# 分析

  • 示例代码中使用了React.memo包装了子组件,可以避免依赖值变化引起的渲染
  • 子组件中一旦有了回调事件,React.memo 就不生效了
  • 点击事件用了useCallback 进行了封装, 但是需要依赖值,依赖值变化了,又会使回调事件变成新的回调事件,进一步造成子组件重新渲染
  • 痛点:使用了公共的对象进行存储数据,但是有时候又不得不这么做

# 解决 使用useEvent

const useEvent = (handler) => {
    const handlerRef = useRef(null);
    useLayoutEffect(() => {
        handlerRef.current = handler;
    });
    return useCallback((...args) => {
        const fn = handlerRef.current;
        return fn(...args);
    }, []);
};
const High = React.memo((props: any) => {
    const { onClick1, high } = props;
    console.log('render high');

    return (
        <>
            身高: {high}
            <Button onClick={onClick1}>身高+1</Button>
        </>
    );
});
const Age = React.memo((props: any) => {
    const { onClick2, age } = props;
    console.log('render age');
    return (
        <>
            年龄: {age}
            <Button onClick={onClick2}>年龄+1</Button>
        </>
    );
});
const Chat = () => {
    const [formData, setFormData] = useState({
        high: 0,
        age: 0,
    });

    const onClick1 = useEvent(() => {
        setFormData({ ...formData, high: formData.high + 1 });
    });
    const onClick2 = useEvent(() => {
        setFormData({ ...formData, age: formData.age + 1 });
    });
    return (
        <div>
            <High onClick1={onClick1} high={formData.high} />
            <Age onClick2={onClick2} age={formData.age} />
        </div>
    );
};

# 总结

  • useLayoutEffect 在render之前同步执行

  • useEffect 在render之后异步执行

  • useCallback 依赖为空,说明函数不会更新

  • handlerRef.current 保存了函数,在useCallback中拿出来用,每次执行的时候能拿到最新的参数

  • useCallback 的作用是让函数不变化

  • useRef.current的作用也是让函数不变化

  • 两者达到相同目的,却又跳出了闭包的陷阱

上次更新: 2022/05/13, 16:14:57
最近更新
01
多分支修复撞车的问题
05-01
02
如何成为架构师
01-23
03
服务器部署全过程
11-23
更多文章>
Theme by Vdoing | Copyright © 2019-2026 fangdown | 粤ICP备19079809号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式