咨询在gatsby-browser.js的replaceRouterComponent中执行GraphQL查询的更佳方案
1. Better Alternatives to Running GraphQL Queries in replaceRouterComponent
You’re right that running GraphQL directly in replaceRouterComponent isn’t ideal—Gatsby’s data layer is optimized for pre-fetching at build time, so let’s look at cleaner approaches:
Use
useStaticQuery/StaticQueryin a Custom Router Wrapper
Create a custom router component that wraps Gatsby’s default Router, then useuseStaticQuery(for function components) orStaticQuery(for class components) to pull your Contentful data here. You can then pass this data down via props or React Context to any components that need it. This way, the data is fetched at build time, not runtime, which is more efficient.Example snippet for a function component wrapper:
import { Router } from "gatsby"; import { useStaticQuery, graphql } from "gatsby"; import { createContext } from "react"; const GlobalDataContext = createContext(); export const CustomRouter = ({ children }) => { const data = useStaticQuery(graphql` query GlobalContentfulData { # Your Contentful query here } `); return ( <GlobalDataContext.Provider value={data}> <Router>{children}</Router> </GlobalDataContext.Provider> ); };Then in
gatsby-browser.js, replace the router with this wrapper:import { CustomRouter } from "./src/components/CustomRouter"; export const replaceRouterComponent = () => CustomRouter;Pre-Generate Global Data in
gatsby-node.js
Use Gatsby’s Node APIs likesourceNodesoronCreateNodeto fetch your Contentful data during the build process, then store it as a custom node or a JSON file. You can later query this pre-processed data usinguseStaticQueryanywhere in your app. This keeps runtime data fetching to a minimum.Leverage Page Queries + React Context
If the data is page-specific, fetch it via page queries (Gatsby’s optimized way) and then pass it up to a Context provider at the page level. Your router or child components can then consume this Context without needing to run their own queries.
2. Fixing the TripAdvisor Component Script Loading Issue
The problem here is likely that this.props.tripAdvisorId isn’t available yet when componentDidMount runs (since it’s fetched asynchronously from your post data). Here’s how to fix it:
Watch for Prop Changes with
componentDidUpdate
Instead of only loading the script on mount, check when thetripAdvisorIdprop updates and load the script once it’s available. Also, add a check to avoid reloading the script unnecessarily.Example for a class component:
import React from "react"; class TripAdvisor extends React.Component { componentDidMount() { this.loadTripAdvisorScript(this.props.tripAdvisorId); } componentDidUpdate(prevProps) { // Only reload if the ID changed and it's not empty if (prevProps.tripAdvisorId !== this.props.tripAdvisorId && this.props.tripAdvisorId) { this.loadTripAdvisorScript(this.props.tripAdvisorId); } } loadTripAdvisorScript = (locationId) => { // Prevent duplicate script loads if (!locationId || document.getElementById("tripadvisor-widget-script")) return; const script = document.createElement("script"); script.id = "tripadvisor-widget-script"; script.src = `https://www.tripadvisor.com/WidgetEmbed?locationId=${locationId}&yourOtherParams=here`; script.async = true; script.onload = () => { console.log("TripAdvisor script loaded successfully"); }; document.body.appendChild(script); }; render() { return <div id="tripadvisor-widget-container"></div>; } } export default TripAdvisor;Convert to a Function Component with
useEffect
If you’re open to using hooks,useEffectmakes this even cleaner by letting you directly watch thetripAdvisorIdprop:import React, { useEffect } from "react"; const TripAdvisor = ({ tripAdvisorId }) => { useEffect(() => { if (!tripAdvisorId) return; const loadScript = () => { if (document.getElementById("tripadvisor-widget-script")) return; const script = document.createElement("script"); script.id = "tripadvisor-widget-script"; script.src = `https://www.tripadvisor.com/WidgetEmbed?locationId=${tripAdvisorId}`; script.async = true; document.body.appendChild(script); }; loadScript(); // Cleanup: Remove script if component unmounts or ID changes return () => { const script = document.getElementById("tripadvisor-widget-script"); if (script) script.remove(); }; }, [tripAdvisorId]); // Re-run effect when ID changes return <div id="tripadvisor-widget-container"></div>; }; export default TripAdvisor;
This ensures the script only loads when the tripAdvisorId is available and avoids duplicate or premature loads.
内容的提问来源于stack exchange,提问作者Darren

