Next.js集成Storyblok React Bridge预览iframe异常重启问题
问题描述
在Next.js项目中使用新版Storyblok React bridge时,_app.js中存在一个路由切换时不会卸载的固定组件,相关代码如下:
function MyApp({ Component, pageProps: { pageData, globalData } }) { const story = useStoryblokState(globalData.story) console.log('rendering app') return ( <> <Collage story={story} /> {/* <Component {...pageData} /> */} </> ); }
对应的getStaticProps代码如下:
export async function getStaticProps() { let sbParams = { version: "draft", // or 'published' }; const storyblokApi = getStoryblokApi(); let { data: pageData } = await storyblokApi.get(`cdn/stories/home`, sbParams); let { data: globalData } = await storyblokApi.get( `cdn/stories/collage`, sbParams ); return { props: { pageData, globalData, }, revalidate: 3600, }; }
注释主组件时功能符合预期:数据变更只会触发Collage组件重渲染,不会触发整页刷新。但取消主组件注释后,在Storyblok预览环境(对接localhost:3000本地服务)中修改任意数据都会导致iFrame重启,表现和未接入React bridge时完全一致,且iFrame重启前数据变更已经实际生效。
问题根因
触发这个问题的核心原因有两点:
- 仅给全局Collage组件对应的
globalData.story添加了useStoryblokState订阅,页面组件Component接收的pageData.story没有接入桥接订阅。新版Storyblok React bridge检测到渲染树中存在未被订阅的Storyblok story数据时,会判定页面没有完成全量实时更新绑定,直接触发iframe硬重载拉取最新内容,保证预览效果和编辑器配置一致。 useStoryblokState直接写在_app.js根组件中,每次story更新会触发整个应用根组件重渲染,导致页面组件状态被重置,和bridge的状态监听逻辑产生冲突,也会触发热更新失效、iframe重载。
修复方案
按以下步骤调整即可解决:
- 把全局固定组件的
useStoryblokState订阅逻辑从_app.js根组件拆到独立的子组件中,避免根组件重渲染影响整个渲染树的状态 - 给页面组件对应的story数据也统一添加
useStoryblokState订阅,保证所有传入渲染树的Storyblok story数据都被bridge接管
调整后的参考代码如下:
首先修改_app.js,移除根组件中的hook调用,拆分独立的全局组件容器和页面渲染容器:
// 独立封装全局固定的Collage组件,单独处理订阅逻辑 function GlobalCollage({ storyData }) { const story = useStoryblokState(storyData) return <Collage story={story} /> } // 统一封装页面组件渲染逻辑,处理页面story的订阅 function PageRenderer({ pageData, Component }) { const pageStory = useStoryblokState(pageData.story) // 保持传给页面组件的props结构和之前一致 const resolvedProps = { ...pageData, story: pageStory } return <Component {...resolvedProps} /> } function MyApp({ Component, pageProps: { pageData, globalData } }) { return ( <> <GlobalCollage storyData={globalData.story} /> <PageRenderer pageData={pageData} Component={Component} /> </> ); }
多页面项目不需要给每个页面单独写订阅逻辑,用上面的统一PageRenderer封装即可。
如果调整后仍然出现iframe重载,检查Storyblok初始化配置,确保bridge功能没有被关闭:
import { storyblokInit, apiPlugin } from "@storyblok/react"; storyblokInit({ accessToken: "项目对应的access token", use: [apiPlugin], bridge: true, // 不要把这个配置设为false components: { // 项目注册的所有Storyblok块组件 } });
内容的提问来源于stack exchange,提问作者fweth
相关产品推荐
相关产品推荐

