最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

plugin development - Invalid hook call on save, not edit when using swiper slider

programmeradmin5浏览0评论

I have installed swiper slider (react version) and created a custom block. When I add the block in the backend it works swimmingly and I can swipe just fine. But when I save and view in the frontend nothing appears. When I check the error messages after refreshing in the backend I see this message:

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app
import { registerBlockType } from "@wordpress/blocks";
import { useBlockProps } from "@wordpress/block-editor";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";

import "./editor.scss";
import "./style.scss";

import json from "./block.json";
import { __ } from "@wordpress/i18n";
const { name, ...settings } = json;

registerBlockType(name, {
    ...settings,
    edit: (props) => {
        return (
            <div {...useBlockProps()}>
                <Swiper
                    spaceBetween={50}
                    slidesPerView={3}
                    onSlideChange={() => console.log("slide change")}
                    onSwiper={(swiper) => console.log(swiper)}
                >
                    <SwiperSlide>Slide 1</SwiperSlide>
                    <SwiperSlide>Slide 2</SwiperSlide>
                    <SwiperSlide>Slide 3</SwiperSlide>
                    <SwiperSlide>Slide 4</SwiperSlide>
                </Swiper>
            </div>
        );
    },
    save: () => {
        return (
            <div {...useBlockProps.save()}>
                <Swiper spaceBetween={50} slidesPerView={3}>
                    <SwiperSlide>Slide 1</SwiperSlide>
                    <SwiperSlide>Slide 2</SwiperSlide>
                    <SwiperSlide>Slide 3</SwiperSlide>
                    <SwiperSlide>Slide 4</SwiperSlide>
                </Swiper>
            </div>
        );
    },
});

I have installed swiper slider (react version) and created a custom block. When I add the block in the backend it works swimmingly and I can swipe just fine. But when I save and view in the frontend nothing appears. When I check the error messages after refreshing in the backend I see this message:

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app
import { registerBlockType } from "@wordpress/blocks";
import { useBlockProps } from "@wordpress/block-editor";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";

import "./editor.scss";
import "./style.scss";

import json from "./block.json";
import { __ } from "@wordpress/i18n";
const { name, ...settings } = json;

registerBlockType(name, {
    ...settings,
    edit: (props) => {
        return (
            <div {...useBlockProps()}>
                <Swiper
                    spaceBetween={50}
                    slidesPerView={3}
                    onSlideChange={() => console.log("slide change")}
                    onSwiper={(swiper) => console.log(swiper)}
                >
                    <SwiperSlide>Slide 1</SwiperSlide>
                    <SwiperSlide>Slide 2</SwiperSlide>
                    <SwiperSlide>Slide 3</SwiperSlide>
                    <SwiperSlide>Slide 4</SwiperSlide>
                </Swiper>
            </div>
        );
    },
    save: () => {
        return (
            <div {...useBlockProps.save()}>
                <Swiper spaceBetween={50} slidesPerView={3}>
                    <SwiperSlide>Slide 1</SwiperSlide>
                    <SwiperSlide>Slide 2</SwiperSlide>
                    <SwiperSlide>Slide 3</SwiperSlide>
                    <SwiperSlide>Slide 4</SwiperSlide>
                </Swiper>
            </div>
        );
    },
});
Share Improve this question asked Dec 26, 2021 at 15:22 user8463989user8463989 5931 gold badge8 silver badges24 bronze badges 2
  • your save function isn't meant to return an operational react component that executes on the frontend, it's meant to return the raw static HTML that gets saved to the database. It's up to you to transform that into a swiper slider on the frontend in JS much like you would a widget or shortcode. There's also a super important part of the error message that you've not shared to try and be helpful which is the stack trace that shows you where and when the calls were made. – Tom J Nowell Commented Dec 26, 2021 at 15:42
  • 1 You might also find that Swiper does not "support" react in the way that you expect it to, it's just a wrapper around a non-React implementation that uses DOM hoisting, so it may not be as portable as you'd hoped. This becomes super obvious when you try to implement nested blocks via a slide block, those slides aren't' recognised as slides and get moved to after the slide area as an "after block" area. I've made PR's to try to and fix this in swiper which improved things, but a fix isn't possible without a re-architecting of how the swiper react code works – Tom J Nowell Commented Dec 26, 2021 at 15:44
Add a comment  | 

1 Answer 1

Reset to default 2

You've misunderstood the purpose of the save component. The goal of the save component is not to return an interactive React component, it's to return something that can be converted into static HTML and saved in the database as a string in the post content.

Since only the HTML gets saved, it's your responsibility to load javascript on the frontend that finds this HTML and converts it into a Swiper slider.

So you should not attempt to do any of the following in the save component, directly or indirectly via a library:

  • event listeners
  • react state
  • callbacks/effects/HTTP requests/etc

A save component can even be ran when no saving is occurring, for example when validating a block by comparing the saved block to the save component.

So with this in mind, it makes no sense to use <Swiper> or <SwiperSlide> components here.

Instead, follow the Swiper documentation and generate actual HTML instead:

<!-- Slider main container -->
<div class="swiper">
  <!-- Additional required wrapper -->
  <div class="swiper-wrapper">
    <!-- Slides -->
    <div class="swiper-slide">Slide 1</div>
    <div class="swiper-slide">Slide 2</div>
    <div class="swiper-slide">Slide 3</div>
    ...
  </div>
  <!-- If we need pagination -->
  <div class="swiper-pagination"></div>

  <!-- If we need navigation buttons -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>

  <!-- If we need scrollbar -->
  <div class="swiper-scrollbar"></div>
</div>

https://swiperjs/get-started#add-swiper-html-layout

Adjust it for JSX and save it that way.


As an aside, the way the Swiper library detects the slides in the <Swiper> component is by inspecting the virtual dom and looking for direct descendants of type <SwiperSlide>. This means it cannot find the slides if you use Slide blocks. This means having user configurable slides is not possible in Gutenberg because of the higher order components used to provide block information. It's not possible to have nested blocks this way. Swiper will interpret child blocks as additional HTML to append after the slider, even if they're slides or <SwiperSlide> components.

发布评论

评论列表(0)

  1. 暂无评论