Contentful SSR React App Getting Error after Hydrating - reactjs

I am making a SSR react contentful application and after I hydrate the app correctly I am getting an error related to the data I got correctly from the server. It thinks the key I am passing to the contentful API does not exist but it did for the server request. I can't quite figure out why it would throw this error this way. Has anyone run into this the code is here minus the keys for obvious reasons.
https://github.com/JoshBowdenConcepts/goop-troop-collective
the current error looks like this:
Uncaught TypeError: Expected parameter accessToken
K contentful.js:53
267 client.js:8
l (index):1
100 main.27827bac.chunk.js:1
l (index):1
r (index):1
t (index):1
<anonymous> main.27827bac.chunk.js:1
contentful.js:53:10
Cheers,

The problem is that while the server-side rendered tree passes siteInfo to <App />, the clientside bundle's index.js does not. If you were running a development build of React, you'd probably see errors related to the hydrated tree differing from the DOM. You'll need to pass the initial props to the client somehow - one popular trick is to inject them into a global variable and pass that, for example:
Server:
getSiteInformation().then((info) => {
const siteInfoInjector = `<script>window.__INITIAL_PROPS = ${JSON.stringify(
info.fields
)};</script>`;
return res.send(
data.replace(
'<div id="root"></div>',
`${siteInfoInjector}
<div id="root">
${ReactDOMServer.renderToString(
<App siteInfo={info.fields} />
)}
</div>`
)
);
});
Client's index.js:
const initialProps = window.__INITIAL_PROPS;
ReactDOM.hydrate(
<React.StrictMode>
<App siteInfo={initialProps} />
</React.StrictMode>,
document.getElementById("root")
);
A word of warning though, this injects the stringified result directly into the HTML, meaning that if your siteTitle is hello </script><script>alert(1), you've got an XSS on your hands. One way you could fix this is by base-64 encoding the initial value:
Server:
const siteInfoInjector = `<script>window.__INITIAL_PROPS = "${Buffer.from(
JSON.stringify(info.fields)
).toString("base64")}";</script>`;
Client:
const initialProps = JSON.parse(atob(window.__INITIAL_PROPS));

Related

How to solve hydration errors related to dates in a React / Remix application?

I'm building an application as a hobby project and as an effort to try and learn server rendered React, but I've stumbled on a seemingly easy to fix error, but I do not know how I should approach the problem. Using Remix 1.10.
While my code runs, it is flawed. The server renders one thing and the client another, causing the rendered element to flicker on pageload. It also throws a multitude of errors in the console, like:
Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
24x react-dom.development.js:12507 Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
react_devtools_backend.js:4012 Warning: Text content did not match. Server: "1/29/2023, 10:44:09 AM" Client: "1/29/2023, 12:44:09 PM"
The server is on UTC timezone but the client can be anything. In this case it's GMT+2. What should I do? I think I could set the server timezone to what the client timezone is but I also think that might be a terrible idea.
The best barebones dumbed down example I could make is this.
// routes/example.tsx
import { useLoaderData } from "#remix-run/react"
import {json, LoaderArgs } from "#remix-run/server-runtime"
export async function loader({ request }: LoaderArgs) {
const timestampFromDB = "2023-01-29T10:44:09.672Z"
return json({ time: timestampFromDB })
}
export default function HydrationError() {
const loaderData = useLoaderData<typeof loader>()
const time = new Date(loaderData.time)
const stamp = time.toLocaleString("en-US")
return (
<div>
Time:
<time>{stamp}</time>
</div>
)
}
I tried to look for answers before asking, but the closest thing I found isn't even close to what my problem is; Remix Hydration failed: UI on server and client do not match. In my case, it's not fine locally, it's not fine at all.
The toLocaleString spec allows output variations across implementations so you're probably better off avoiding the client's implementation and just using the server's implementation by moving toLocaleString to the loader.
// routes/example.tsx
import { useLoaderData } from "#remix-run/react"
import {json, LoaderArgs } from "#remix-run/server-runtime"
export async function loader({ request }: LoaderArgs) {
const timestampFromDB = "2023-01-29T10:44:09.672Z"
return json({ stamp: new Date(timestampFromDB).toLocaleString('en-US') })
}
export default function HydrationError() {
const { stamp } = useLoaderData<typeof loader>()
return (
<div>
Time:
<time>{stamp}</time>
</div>
)
}
Alternatively you might want to look at Intl.DateTimeFormat which gives you greater control over date rendering and may offer more consistency.
React Intl is a library built on top of Intl.DateTimeFormat which is worth checking out.

How to render a shared post's tilte and tags when shared in social platforms or other applications?

I need to render the title of a post when it's shared via Facebook or in other apps or messages.
I want Something like this:
However, my posts show the actual title of the application, which is not what I want. When shared, I want the exact title of the posts to be dynamically displayed as per the post's meta-tags, not the main description of my application
Do not want something like in the picture below
Using React-helmet
To render the post's title, I am using React-helmet async to change the title and meta tag as client-side rendering at runtime. The changes are visible during post-inspection but not on the page source (Ctrl + U on the post) and social media shares.
My index.html
<title>my title</title>
<meta
name="description"
content="test description"
/>
My code on one of the pages
<Helmet>
<title>{title}</title>
<meta name='description' content={description} />
</Helmet>
I then wrap app.js with the react-helmet as
<HelmetProvider>
<App/>
</HelmetProvider>
You can replicate the issue (based on react-helmet) from this link:
https://preview-react-helmet-meta-ta.samsara-web.pages.dev/discussions/discussions-details/419
Can this issue be fixed with React-helmet?
React-snap
This is another package that has not been updated in the last four years.
To change the source file, it must be rendered from the server. I used react-snap to pre-render the HTML file, however, got the following issue.
The build folder is ready to be deployed.
19:36:10.984 You may serve it with a static server:
19:36:10.984
19:36:10.984 yarn global add serve
19:36:10.984 serve -s build
19:36:10.984
19:36:10.984 Find out more about deployment here:
19:36:10.985
19:36:10.985 https://cra.link/deployment
19:36:10.985
19:36:11.124 $ react-snap
19:36:13.427 🔥 pageerror at /: SyntaxError: Unexpected token '<'
19:36:13.427
19:36:13.528 ️️️💬 console.log at /: Buffered flag does not support the 'longtask' entry type.
19:36:13.634 ️️️💬 console.log at /: ServiceWorker registration successful with scope: http://localhost:45678/
19:36:13.723 ️️️⚠️ warning at /: got 403 HTTP code for https://accounts.google.com/gsi/client
19:36:13.724 ️️️💬 console.log at /: Failed to load resource: the server responded with a status of 403 ()
19:36:13.733 ️️️💬 console.log at /: An <img> element was lazyloaded with loading=lazy, but had no dimensions specified. Specifying dimensions improves performance. See https://crbug.com/954323
19:36:14.638 ️️️💬 console.log at /: Access to XMLHttpRequest at 'https://cloudflareinsights.com/cdn-cgi/rum' from origin 'http://localhost:45678' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'http://localhost' that is not equal to the supplied origin.
19:36:14.639 ️️️💬 console.log at /: Failed to load resource: net::ERR_FAILED
19:36:19.024 ✅ crawled 52 out of 52 (/)
19:36:19.083
19:36:19.107 error Command failed with exit code 1.
19:36:19.107 info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
19:36:19.127 Failed: build command exited with code: 1
19:36:20.854 Failed: an internal error occurred
My package.json
"scripts": {
"postbuild": "react-snap"
}
My index.js
const MyApp = () => (
<Provider store={store}>
<SWRConfig
value={{
fetcher,
dedupingInterval: 10000,
onError: (error, key) => {
if (error.status !== 403 && error.status !== 404) {
// TODO Implement sentry integration
}
},
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
if (error.status === 404) return
if (retryCount >= 7) return
setTimeout(() => revalidate({ retryCount }), 5000)
}
}}
>
<ErrorBoundary>
<App />
</ErrorBoundary>
</SWRConfig>
</Provider>
)
const rootElement = document.getElementById('root')
if (rootElement.hasChildNodes()) {
ReactDOM.hydrate(<MyApp />, rootElement)
} else {
ReactDOM.render(<MyApp />, rootElement)
}
On further research, I found that server-side rendering can fix this issue. However, it is challenging to render React apps components server-side. There was a potential solution of using an Express Server with complex configurations, discussed here https://blog.logrocket.com/adding-dynamic-meta-tags-react-app-without-ssr/ .
Currently, I am using React and React-dom version 17.0.2.
One of the example posts one can use for inspection would be https://samsara.social/discussions/discussions-details/215/the-dark-triad-in-primates-mind-machiavellian-inte
or any other post on samsara.social web app.
You have to implement open-graph-tags which has too many types.
<Helmet>
// dynamically creating title and helmet will inject it
<title>{title}</title>
// og stands for open graph
<meta property="og:title" content="Users app" />
</Helmet>
For every article page your meta title and description need to be change with your article title and description so it will render it while you share article links. pass meta tags content properly from your page.

React Storybook unable to render a story

I am trying to create documentation with a storybook folder containing topics. But getting this error:
Uncaught Error: Unable to render story ... as the component annotation is missing from the default export
Migration.stories.mdx
import { Meta, Story} from '#storybook/addon-docs';
<Meta title="Documentation / Visual Update Migration" />
# Migrating
<Story name="Card" />
## Card
- Text
- Text
<Story name="Container" />
## Container
- Text
- Text
All these cause Story marks, but if I remove them, there won't be refs in the sidebar
Expected:
So, the path and side bar load correctly, but the content on click does not load giving the error.
Uncaught Error: Unable to render story documentation-visual-update-migration--card as the component annotation is missing from the default export ...
Uncaught Error: Unable to render story documentation-visual-update-migration--container as the component annotation is missing from the default export ...
Plus
2 The above error occurred in the <storyFn> component ...
And
POST http://localhost:6006/runtime-error 404 (Not Found)
POST http://localhost:6006/runtime-error 404 (Not Found)
The problem was in <Story ... />. Story wants something inside it.
Fix: <Story ...>{''}</Story>

Getting "Unsafe attempt to load URL data:image/svg+xml..." in Safari using a through React component

I'm using rollup to bundle a react npm package that contains an icon component that takes a name as a prop and returns an Icon with that name wrapped by a react component.
This is the component code:
import sprite from './public/sprite.svg';
function Icon({ name }) {
return <svg className="svg-wrapper">
<use href={`${sprite}#${name}`} />
</svg>
);
}
The folder structure is the following:
- src
- - public
- - - sprite.svg
- - icons
- - - some-icon.svg
- - - some-other-icon.svg
- - index.tsx # component with the code mentioned above
And this is my rollup config:
export default {
plugins: [
esbuild({
sourceMap: false,
target: "esnext"
}),
image(),
svgicons({
inputFolder: "src/icons",
output: "public/sprite.svg"
}),
json()
]
}
This works fine in Chrome (although it does inline all the svg inside of the href which I think it's the purpose of this approach) but in Safari it triggers the following error:
Unsafe attempt to load URL data:image/svg+xml,%3c%3fxm ....
Domains, protocols and ports must match.
The thing is, as mentioned, this is an npm package that packages the icons as part of the js bundle (inlining) so there's not much control over how this component is served since this is handled by the browser caching (also one of the key points of using this approach). I'm quite familiar with CORS and I know that perhaps avoiding to use data:image/svg+xml uri links would fix this but would increase the complexity of the build steps of this package (needing to build the icons using svgr/svgo and then have some kind of lookup table to give back the right icon based on the name prop i.e.).
So, ultimately my question is, with the sprite approach in a react component library is there a foolproof way of avoiding these kind of issues and cross-browser inconsistencies?
Thanks in advance for any help provided.
I have been struggling with this issue for a while. I guess this is a bug on Safari throwing an error because is dealing with a dataURI as if it was an external URL.
About your code, you could expose your sprite in a public folder or publish it in a cdn (good for caching purposes) and change the way rollup is handling your svg (it seems it is packing your svg as a dataURI). Alternatively, I implemented a workaround to convert the dataURI in a blob.
import sprite from './public/sprite.svg';
function dataURItoBlobUrl(dataURI: string) {
const svg = decodeURI(dataURI).split(',')[1];
const blob = new Blob([svg], { type: "image/svg+xml" });
return URL.createObjectURL(blob);
}
const blobUrl = dataURItoBlobUrl(sprite);
export const Icon: FC<IconProps> = ({ name, ...props }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" {...props}>
<use href={`${blobUrl}#${name}`}></use>
</svg>
);
};

firebase serve - React Context returns undefined

I'm currently facing a problem with a firebase deployment, happening only in production environment. I cannot reproduce it in local, with the firebase serve command; even if I'm using the target 6.11.5 node version.
Here is my trouble : when deployed, the site will crash (server-side) rendering a React component using a Context's return property
Component extract :
<div className='rank-item col-sm-6 col-lg-3'>
<LangContext.Consumer>
{
({ lang, langs }) => (
// here, props are cools in development
// but are undefined in production
)
}
</LangContext.Consumer>
</div>
App root component (at this point, props.lang & props.langs are correctly set, even on production environment) :
export default props => (
<LangContext.Provider value={ {
lang: props.lang,
langs: props.langs
} }>
</LangContext.Provider>
)
The problem as shown in the console :
As you may have guess, I'm building my SSR code with webpack to commonjs2 module. The error stack trace here refers to this exact line of built code :
I'm a bit surprised because I already used React's Context feature on a production environment, but cannot figure now what could be the problem. Thanks for your help

Resources