JSX fragment content is not rendering - reactjs

[postslug].js
import {PostData} from '../../data/postdata'
export async function getStaticProps({ params }) {
const posts = PostData.find((p) => p.slug === params.postslug);
return {
props: {
post: posts,
},
};
}
export async function getStaticPaths() {
const paths = PostData.map((post) => ({
params: { postslug: post.slug },
}));
return { paths, fallback: false };
}
const Post = ({post}) => {
// const router = useRouter();
// const slug = router.query.postslug;
// const post = PostData.find((post1) => post1.slug === slug);
return (
<>
{post.content}
</>
)
}
PostData.js
export const PostData = [
{
id: 1,
slug: "article-blog",
content: `<><main>
<div className="postpage">
<section className="article">
<article>
<h2>Google Chrome</h2>
<p>Google Chrome is a web browser developed by Google, released in 2008. Chrome is the world's most popular web browser today!</p>
</article>
<article>
<h2>Mozilla Firefox</h2>
<p>Mozilla Firefox is an open-source web browser developed by Mozilla. Firefox has been the second most popular web browser since January, 2018.</p>
</article>
<article>
<h2>Microsoft Edge</h2>
<p>Microsoft Edge is a web browser developed by Microsoft, released in 2015. Microsoft Edge replaced Internet Explorer.</p>
</article>
</section>
</div>
</main></>`
},
]
The code written in JSX Fragments is not rendering after fetching it. It is just displayed as it is written in an array of objects. Postdata.js file is containing an array of objects. I am trying to fetch the data of blog articles using getStaticProps and getStaticPaths.
Output Like:

The first solution can be using dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{__html: post.content}} />
But as for the security problem, it will lead you to cross-site scripting (XSS) attack. So I'd propose you use html-react-parser that will help you to render a string as JSX safely.
import parse from 'html-react-parser';
const Post = ({post}) => {
return (
<>
{parse(post.content)}
</>
)
}

You have to use dangerouslySetInnerHTML in React, it is equivalent to seting innerHTML in vanilla JS refer this React.js: Set innerHTML vs dangerouslySetInnerHTML

Related

getStaticProps returns undefined, but the same code works in create-react-app [duplicate]

This question already has answers here:
NEXTJS: getServerSideProps not working into components
(2 answers)
Closed 8 months ago.
I've converted my create-react-app to a next js app to start SSG, I tested the getStaticProps function with a jsonplaceholder API and I was able to retrieve the data and display it, but for some reason it keeps returning undefined when I changed the api.
Here is the code
import React, { useState } from 'react';
export const getServerSideProps = async () => {
const response = await fetch(`https://sample.firebaseio.com/Properties.json?orderBy="/records/0/Address/City"&equalTo="City"`);
const getData = await response.text();
const parsedData = JSON.parse(getData);
const keyName = Object.entries(parsedData);
return {
props: { properties: keyName }
}
}
const PropertiesByCity = ({ properties }) => {
const [getProperties, setProperties] = useState([]);
setProperties(properties);
return (
<div className="map__wrapper">
{getProperties.map(([key, value]) => {
try {
return (
<div key={key} className="listing__wrapper">
<div key={key} className="property__main-photo">
<img key={key} src={value.records[0].Photo.PropertyPhoto[0].PhotoURL} />
<h1 key={key}>{new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(value.records[0].Price).slice(0, -3)}</h1>
<h1 key={key}>{value.records[0].Address.StreetNumber} {value.records[0].Address.StreetName}</h1>
<p key={key}>{value.records[0].Address.City}</p>
</div>
<div key={key} className="listing__info">
<div key={key} className="listing__info-bedbath">
{value.records[0].Building.BedroomsTotal}
<p className="listing__info-bedbath-bottom">Bedrooms</p>
</div>
<div key={key} className="listing__info-bedbath">
{value.records[0].Building.BathroomTotal}
<p className="listing__info-bedbath-bottom">Bathrooms</p>
</div>
</div>
</div>
);
} catch (error) {
return (<p> </p>)
}
}
)}
</div>
);
};
export default PropertiesByCity;
The JSON that is returned from calling this api looks something like this:
{"records":[{"Address":{"AddressLine1":"1234 Sample ","City":"Stoney Point","Country":"Canada","Latitude":"0000000","Longitude":"0000000","PostalCode":"11111","Province":"Ontario","StreetAddress":"1234 Sample","StreetName":"ST. CLAIR","StreetNumber":"1234"},"Phones":{"Phone":[{"_XmlAttributes":{"ContactType":"Business","PhoneType":"Telephone"},"_XmlContent":"(229) 999-2223"},{"_XmlAttributes":{"ContactType":"Business","PhoneType":"Fax"},"_XmlContent":"(111) 111-1111"},{"_XmlAttributes":{"ContactType":"Business","PhoneType":"Toll Free"},"_XmlContent":"(111) 113-1113"}]},"PhotoLastUpdated":"Wed, 01 Sep 2021 15:22:00 GMT","Position":"Sales Person","Websites":{"Website":{"_XmlAttributes":{"ContactType":"Business","WebsiteType":"Website"},"_XmlAttributes":{"ID":"12334566"}},"AlternateURL":{"VideoLink":"https://youtu.be/D-kfhdskjfh"},"Board":"34","Building":{"ArchitecturalStyle":"Ranch","BathroomTotal":"2","BedroomsAboveGround":"3","BedroomsBelowGround":"0","BedroomsTotal":"3","ConstructionStyleAttachment":"Detached","ExteriorFinish":"Stone, Concrete/Stucco","FireplacePresent":"False","FlooringType":"Hardwood, Cushion/Lino/Vinyl","FoundationType":"Concrete","HeatingFuel":"Natural gas","HeatingType":"Forced air","Rooms":{"Room":[{"Dimension":"Measurements not available","Length":"","Level":"Basement","Type":"Utility room","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Basement","Type":"Storage","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"4pc Bathroom","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"5pc Ensuite bath","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Laundry room","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Bedroom","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Primary Bedroom","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Living room","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Eating area","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Kitchen","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Foyer","Width":""}]},"StoriesTotal":"1","Type":"House"},"Business":{"Franchise":""},"Features":"Double width or more driveway, Gravel Driveway","Land":{"Acreage":"false","SizeIrregular":"109XIRREG FT","SizeTotalText":"109XIRREG FT"},
The JSON is a series of objects that are similar to this, but not pasting all of them, you get the point.
The code was working just fine with a useEffect in my react app, and this file worked fine when I tested it with a JSONplaceholder API, but for some reason it isn't working when I use my firebase api. I can't seem to find a better way to debug this using console.log() like I can in a client side rendered app.
I am calling this component with a in my index.js file.
I can't seem to debug with console.logs either, it is just returning undefined.
If someone could give me an answer, or a better way to test the app to debug this problem, please let me know.
The answers that I've seen have just stated that the getStaticProps should only be called in the index.js file, but it worked when using the JSonplaceholder API.
I was calling the component in my index.js file, instead of visiting the route in the components file. If I would've went to the URL localhost:3000/components/pull-city, the getStaticProps would have triggered and rendered the data.
I am still getting used to static websites, I am not familiar with the differences in directory configurations, but I'm getting there.

Building query with NextJS, Prisma, and ClerkJS

I'm prototyping a project using NextJS, Prisma, and ClerkJS. I'm trying to understand how I would supply various params/props to my Prisma search clause. In particular I need to get the email address of a user from ClerkJS. This is my current index file:
import React from "react";
import prisma from "../../prisma/initPrisma"
const FacilitiesPage = ({ facilities }) => {
return (
<div className={styles.dashCards}>
{facilities.map((facility) => {
return (
<div className={styles.card} key={facility.id}>
<h4>{facility.name}</h4>
</div>
);
})}
</div>
);
};
export async function getStaticProps() {
const facilities = await prisma.facility.findMany({
where: {
ownerEmail: 'harcodedemail'
},
});
return {
props: {
facilities,
},
};
}
export default FacilitiesPage;
Obviously I can't hardcode the email address of every user in the system. ClerkJS offers several ways to query the user object and return various things from it, which I could pass into getStaticProps (or getServerSideProps probably). But nothing I've tried works. Candidly, I'm still learning the "React way" to do a lot of things.
TL;DR: how do I supply props to the query string in getStaticProps?
The folks at Clerk.dev just answered this question. You need to use getServerSideProps and then use the new "withServerSideAuth" component. Here is a snippet from the blog post https://clerk.dev/blog/next-js-ssr-authentication-with-clerk :
import { withServerSideAuth } from "#clerk/nextjs/ssr";
export const getServerSideProps = withServerSideAuth(async ({ req, resolvedUrl }) => {
const {sessionId,getToken} = req.auth;
if (!sessionId) {
return { redirect: { destination: "/sign-in?redirect_url=" + resolvedUrl } };
}
// use a token for your Clerk integrations
const hasuraToken = await getToken({ template: 'hasura' });
// retrieve data from your Hasura integration
return { props: {} };
});

getStaticProps returns undefined when exporting | NextJS

I'm working on a blog with nextjs. I want to export it as a static website.
My problem is that I want to make a dynamic routing (for multiple posts) so I created a [post].js file under a blog page (which contains only getStaticProps but works correctly).
When building my app, everything is Ok, when running the app (yarn next) everything is also Ok. But when I try to yarn export in order to deploy the website, I get this error :
Error: Error serializing `.title` returned from `getStaticProps` in "/blog/[post]".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
... etc
Error: Export encountered errors on following paths:
/blog/[post]
I tried to simplify my code as much as possible. All I do is to show the message I pass on props, but even like this, it does not work. All the other components are just esthetical components, so I really don't understand what's the problem. Here's the code of [post].js:
const Post = props => {
const [enableParticles, setEnableParticles] = React.useState(true);
return (
<div className="App">
<Header blog />
<Layout>
<SocialMediaNav />
<ParticlesNetwork enableParticles={enableParticles} />
<ToggleButton
checked={enableParticles}
onClick={() => setEnableParticles(!enableParticles)}
/>
<SectionWrapper id="blog-homepage" offset={0} minHeight="90vh">
{props.title}
</SectionWrapper>
</Layout>
<Footer />
</div>
);
};
export async function getStaticPaths() {
return {
paths: [
{ params: { post: 'mes_notes_en_probabilites' } }
],
fallback: false,
};
}
export async function getStaticProps(context) {
return {
props: {
title: context.params.post
},
};
}
export default Post;
note: i tried to do as it was said on this tutorial: https://dev.to/akuks/what-is-getstaticpaths-in-nextjs-5ee5
Do you have an idea?

Showing 404 error after hosting a webapp in react js

I have created a Blog web App using react Js , it is working fine on my local system but after hosting it ,it is showing 404 status code . I have used fake json-server package to launch on my local system but after hosting it with the github it is fetching no data from json file.
This is the webapp https://manishya1669.github.io/BlogWebApp/ and this is the code for Blogdetails and for more clearity you can see the whole code source on https://github.com/manishya1669/BlogWebApp
import { useHistory, useParams } from "react-router-dom";
import useFetch from "./useFetch";
const BlogDetails = () => {
const {id} = useParams();
const {data:blog, error,isPending} = useFetch('http://localhost:8000/blogs/' + id);
const history = useHistory();
const handleClick = () => {
fetch('http://localhost:8000/blogs/' + blog.id, {
method: 'DELETE'
}).then(() => {
history.push('/');
})
}
return (
<div className= "blog-details">
{isPending && <div> Loading</div> }
{error && <div>{error}</div>}
{ blog &&(
<article>
<h2>
{blog.title}
</h2>
<p> Written by {blog.author}</p>
<div>{blog.body}</div>
<button onClick={handleClick}>Delete</button>
</article>
)
}
</div> );
}
export default BlogDetails;
**Hope this is enough to make my self clear and appreciating those who are willing to help**
I found its solution , it was not following Cors policy because
I was running my whole Client side on port no 3000 and trying to receive data from port 8000, So due to two different port it was considering two different website where one is trying to retrieve data from other also that website was not secure . So to deal with this you have add a proxy server in your package.json for example
"name": "webapp",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:8000",
Also you have to replace fetch('http://localhost:8000/blogs/' + blog.id,
with fetch('/blogs/' + blog.id,
const handleClick = () => {
fetch(/'blogs'/+ blog.id, {
method: 'DELETE'
}).then(() => {
history.push('/');
})
}
If you find and any mistake please let me know it would help me to learn more.

Vercel: ERROR Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild

Hope you all are having a good time. I am working on a simple NextJs application where I want to have multiple subdomains in it. I am deploying the application on vercel.
What my application does is it have a simple textarea where you write MDX, you click the Publish button and it will save that MDX into firebase firestore. Below the textarea it shows the list of all the pages that have been published before.
The application renders the list of all the pages like with name of the page which is randomly generated as the subdomain while the actual domain comes later like the following.
a-mdx-page.mydomain.app
When I open that URL it will fetch the page MDX from firestore and uses next-mdx-remote package to serialize and render the MDX. The reason for using the next-mdx-remote package is that we can add react components in MDX and it can render as normal react components. I already have a custom domain because you cannot have a subdomain on top of a free subdomain in vercel free deployment.
All works fine on localhost and everything is as it should be but the problem is when I deploy the code on Vercel and navigate to subdomain it shows ERROR 500 on the website and shows the following error in the logs.
[GET] / 21:21:03:30
2021-10-24T16:21:04.018Z 8e52d5da-ff1f-4840-a09b-199233834a5d ERROR Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild.
If you are installing esbuild with npm, make sure that you don't specify the
"--no-optional" flag. The "optionalDependencies" package.json feature is used
by esbuild to install the correct binary executable for your current platform.
at generateBinPath (/var/task/node_modules/esbuild/lib/main.js:1643:15)
at esbuildCommandAndArgs (/var/task/node_modules/esbuild/lib/main.js:1699:11)
at ensureServiceIsRunning (/var/task/node_modules/esbuild/lib/main.js:1856:25)
at Object.transform (/var/task/node_modules/esbuild/lib/main.js:1751:37)
at serialize (/var/task/node_modules/next-mdx-remote/dist/serialize.js:287:43)
at async getServerSideProps (/var/task/.next/server/pages/index.js:261:25)
at async Object.renderToHTML (/var/task/node_modules/next/dist/server/render.js:428:24)
at async doRender (/var/task/node_modules/next/dist/server/next-server.js:1144:38)
at async /var/task/node_modules/next/dist/server/next-server.js:1236:28
at async /var/task/node_modules/next/dist/server/response-cache.js:64:36 {
page: '/'
}
RequestId: 8e52d5da-ff1f-4840-a09b-199233834a5d Error: Runtime exited with error: exit status 1
Runtime.ExitError
From what I understand that the next-mdx-remote serialize function uses esbuild in it and when I deploy the application on vercel npm just doesn't downloads the platform specific package of it but may be I am wrong.
I have tried to search the solution for this but there is not any answers that helped me.
Following is all the code that the application uses.
import { useState } from "react"
import { collection, doc, getDoc, getDocs, setDoc } from "firebase/firestore"
import matter from "gray-matter"
import { MDXRemote } from "next-mdx-remote"
import { serialize } from "next-mdx-remote/serialize"
import {
uniqueNamesGenerator,
adjectives,
colors,
animals,
} from "unique-names-generator"
import { db } from "../utils/fire-client"
import Layout from "../components/Layout"
import { HOSTNAME } from "../config"
import MDXComponents from "../components/mdx"
export default function Index({ posts, isPage = false, mdxSource }) {
const [mdxCode, setMdxCode] = useState("# THIS IS MDX")
const [message, setMessage] = useState("")
const addPageToCollection = async (name, content) => {
const pagesCollection = collection(db, "pages")
await setDoc(doc(pagesCollection, name), {
name,
content,
})
}
function publishPage() {
const randomName = uniqueNamesGenerator({
dictionaries: [adjectives, colors, animals],
})
addPageToCollection(randomName, mdxCode)
setMessage(
"New Page Added: " + randomName + "\nReload page To see it in the list"
)
setTimeout(() => {
setMessage("")
}, 5000)
}
return (
<Layout>
{isPage ? (
<>
<header>
<nav>
<a href={"http://" + HOSTNAME}>
<a>👈 Go back home</a>
</a>
</nav>
</header>
<main>
<MDXRemote {...mdxSource} components={MDXComponents} />
</main>
</>
) : (
<>
<h1>Home Page</h1>
<textarea
name="mdxCode"
id="mdxCode"
value={mdxCode}
onChange={(e) => setMdxCode(e.target.value)}
className="w-full h-1/2 border-2 border-gray-400 p-2"
/>
<button className="btn btn-primary" onClick={publishPage}>
Publish
</button>
<div>{message}</div>
<ul>
<div className="mt-4 font-bold">Pages List</div>
{posts.map((post) => (
<li key={post.name}>
<a href={`http://${post.name}.${HOSTNAME}`}>{post.name}</a>
</li>
))}
</ul>
</>
)}
</Layout>
)
}
export async function getServerSideProps({ req, res }) {
const host = req.headers.host.split(".")
if (host[0] !== HOSTNAME.split(".")[0] && host[0] !== "www") {
const docRef = doc(db, "pages", host[0])
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
const { content, data } = matter(docSnap.data().content)
const mdxSource = await serialize(content, {
// Optionally pass remark/rehype plugins
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [],
},
scope: data,
})
if (mdxSource) {
return {
props: {
isPage: true,
mdxSource,
},
}
}
} else {
return {
props: {
redirect: {
destination: "/",
},
},
}
}
}
const pagesCollection = collection(db, "pages")
const pagesSnapshot = await getDocs(pagesCollection)
const pagesList = pagesSnapshot.docs.map((doc) => doc.data())
if (pagesList.length > 0) {
return {
props: {
posts: pagesList,
},
}
}
return { props: { posts } }
}
Update esbuild to 0.13.4 or higher
npm i -D esbuild#0.13.4
See: https://github.com/evanw/esbuild/releases/tag/v0.13.4
If building with Docker this could be due to an incomplete .dockerignore file that doesn't ignore all your node_modules folder.

Resources