属性更新时销毁并重新初始化Swiper实例报错问题排查
问题
在开发Gutenberg滑块区块时,使用以下代码初始化Swiper:
const containerRef = useRef(null); const swiperRef = useRef(null); const { attributes: { images, fx, auto }, setAttributes, } = props; useEffect(() => { if (swiperRef.current) { console.log(swiperRef.current); // Destroy Swiper instance when updating. swiperRef.current.destroy(true, true); } // Init Swiper instance. swiperRef.current = new Swiper(containerRef.current, { modules: [Autoplay, Navigation, EffectFade], loop: true, effect: fx, fadeEffect: { crossFade: true }, centeredSlides: false, autoHeight: false, observer: true, autoplay: auto ? { delay: 3000, disableOnInteraction: false, pauseOnMouseEnter: true, } : false, navigation: { nextEl: '.slider-navigation .slider-next', prevEl: '.slider-navigation .slider-prev', } }); }, [images, fx, auto]);
每当依赖数组中的属性变化时,useEffect钩子会执行。虽然已经在销毁实例前检查了swiperRef.current是否存在,但首次运行时(还未完成Swiper初始化),该条件为true,调用swiperRef.current.destroy(true, true)抛出错误:
Uncaught TypeError: r is null R http://example.com/wp-content/plugins/my-slider/build/blocks/slider/index.js?ver=20f7ca98c8222f0d1ea3:1 detachEvents http://example.com/wp-content/plugins/my-slider/build/blocks/slider/index.js?ver=20f7ca98c8222f0d1ea3:1 destroy http://example.com/wp-content/plugins/my-slider/build/blocks/slider/index.js?ver=20f7ca98c8222f0d1ea3:1 edit http://example.com/wp-content/plugins/my-slider/build/blocks/slider/index.js?ver=20f7ca98c8222f0d1ea3:1 commitHookEffectListMount http://example.com/wp-includes/js/dist/vendor/react-dom.js?ver=18.2.0:23160 commitPassiveMountOnFiber http://example.com/wp-includes/js/dist/vendor/react-dom.js?ver=18.2.0:24936 commitPassiveMountEffects_complete http://example.com/wp-includes/js/dist/vendor/react-dom.js?ver=18.2.0:24901
console.log(swiperRef.current)显示存在Swiper实例但未初始化,请问如何判断这种情况,避免首次运行时调用destroy方法?
解决方法
可以通过两种方式判断Swiper实例是否已完成初始化,从而避免首次执行时调用destroy:
方法1:检查实例的initialized属性
Swiper实例初始化完成后会自动添加initialized属性,值为true。修改判断条件,只有当实例存在且已初始化时才执行销毁:
if (swiperRef.current && swiperRef.current.initialized) { swiperRef.current.destroy(true, true); }
方法2:使用额外的状态标记初始化状态
在组件中添加一个状态变量,标记是否已经完成过一次Swiper初始化,首次执行时跳过销毁逻辑:
const [isSwiperInitialized, setIsSwiperInitialized] = useState(false); useEffect(() => { if (isSwiperInitialized && swiperRef.current) { swiperRef.current.destroy(true, true); } swiperRef.current = new Swiper(containerRef.current, { // 原有配置项保持不变 }); // 初始化完成后更新状态标记 setIsSwiperInitialized(true); }, [images, fx, auto]);
额外注意事项
- 可以在
useEffect开头添加if (!containerRef.current) return;,确保DOM容器挂载后再初始化Swiper,避免无效实例创建。 - 确认Swiper的依赖模块(如
Autoplay、Navigation)已正确导入,防止初始化异常。
内容的提问来源于stack exchange,提问作者leemon
相关产品推荐
相关产品推荐

