React hook for Facebook Comment - preload - node.js

How to pre-load an iframe with React hook.
Recently i implemented a Facebook Comment Tool on my page. https://jackylenghia.com/#/Contact
However, look like iframe requires a preload (i.e. in ComponentDidMount in old React), so the iframe does not appear, but you will need to refresh the page (F5 or Cmd + R), to let the iframe loaded.
How should I fix this with React Hook.
I tried to by loading this with useEffect but does not help
Original:
const Contact = (props) => {
return(
... //Other stuffs
<Col>
<div class="fb-comments" data-href="https://jackylenghia.com/#/Contact" data-numposts="5" data-width=""></div>
</Col>
...
)
}
What I tried
const Contact = (props) => {
const [fbcomment, setfbcomment] = useState(0);
useEffect(() => {
setfbcomment(`<div class="fb-comments" data-href="https://jackylenghia.com/#/Contact" data-numposts="5" data-width=""></div>`);
});
return(
... //Other stuffs
<Col>
{fbcomment}
</Col>
...
)
}
But this only print out a text string only, instead of inserting the element to dom

Turn out, to preload the iframe, I only need to run this from FB JavaScript SDK, in useEffect:
useEffect (() => {
FB.XFBML.parse();
});
This will request FB component to be parsed after dom loaded.

Related

Need to call an api for each key stroke in react, but the response can have thousands of objects

I am using react and axios for frontend, and nextjs with prisma for backend. I have in the database 4000 exercices that contain fitness exercices. I want to create a function where by each key stroke, the api will look for the relevant exercice. I finished creating it, but i have some issues:
The main problem is that the response is delayed from the first keystrokes, because the payload response is tooo large. I created a scrollable UL element to render the elements, because I want to get also the Gif images. So the elements, if the API will find those, will be rendered on the screen.
If I add to each element an on click event, to select the exercice's Id, I get an error "too many re-rendering on the screen".
How can I optimise the function, and how can I solve the error of too many re-render on the screen? Nextjs tells me that it will create an infinite loop....
The frontend looks like this:
const [open, setOpen] = useState(false);
const [keyWord, setKeyWord] = useState('');
const [array, setArray] = useState([]);
const [exerciceId, setExerciceId] = useState('');
// Add exercice
const hadnleAddExercie = async event => {
event.preventDefault();
console.log('exercice added');
}
// Look for exercices
const searchExercices = async event => {
event.preventDefault();
setKeyWord(event.target.value);
const arrayExercices = await getExercicesByKeyWords(keyWord);
setArray(arrayExercices);
console.log(arrayExercices);
}
<div className='flex mt-3 flex-col'>
<input onChange={searchExercices} required placeholder='Search by word...' className='border border-slate-400 p-1 rounded-md flex-1 max-w-sm my-2'/>
<ul className='border border-slate-400 p-1 rounded-md max-w-sm my-2 max-h-52 overflow-scroll'>
{
array.length > 1 && array.map(exercice => (
<li key={exercice.id} className='flex flex-wrap p-2 bg-slate-200 m-2 items-center rounded-md'>
<span><Image className='rounded-xl mr-2' priority width={40} height={40} src={exercice.gifUrl} alt={exercice.name}/></span>
<span>{ exercice.name }</span>
</li>
))
}
</ul>
</div>
The backend Uses prisma and I use the OR clause to look for a word in different rows:
export default async function handler(req, res) {
try {
const param = req.query.slug[0];
console.log(param);
// Get exercices where the two rows contains a single parametter
const exercices = await prisma.exercices.findMany({
where: {
OR: [
{
name: {
contains: param
}
},
{
target: {
contains: param
}
},
{
equipment: {
contains: param
}
}
]
}
});
res.status(200).send(exercices);
}
catch (error) {
console.log(error);
res.status(500).send(error);
}
}
An example can be this:
Only for finding an exercice I used 500mb...
Here are a few ways I can think of to optimize this:
Use pagination and fetch more results as user scrolls down or actually separate it by using pages. You can read more on how to implement pagination in Prisma here.
Add debounce to your search term so it doesn't actually fire on every single keystroke, you could use something like useDebounce.
Use React.memo to prevent the list from being re-rendered every time some state changes, only re-render it when the actual list changes.

Load specific DIV with a react component without reloading the whole page

I have a menu where every menu item is a button and I want to load a specific reactjs component into a specific div without reloading the whole page.
This is the current code, clearly is bad but I don't know where to start fixing it...
...
<Button onClick={this.loadTarget}>
{menuItem.name}
</Button>
...
loadTarget(event) {
document.getElementById("datapanel").innerHTML="abc<TranslationsList />";
}
When I click a menu Item I want to load my div with the value "abc<TranslationsList />". "abc" is displayed but the custom component "TranslationsList" is not and I guess this is normal as the TranslationsList tag is not a HTML tag. But how could I load my component?
I could use links instead of buttons but in this case the question is how could I update the div content with a specific link?
It's hard if you've programmed plain JS before, but you have to forget the "good old JS pattern" in React. I also had a hard time getting used to not using standard JS elements (target, innerHTML, etc.) to solve such a problem.
So the solution in React is to use the framework and your page reload problem will be solved immediately. useState for the state of the component and handlers for the click. My main code looks like this. You can find a working application at Codesandbox.
export default function App() {
const [showComponent, setShowComponent] = useState(false);
const handleButtonClick = (e) => {
setShowComponent(!showComponent);
};
return (
<div className="App">
<h1>
Load specific DIV with a react component without reloading the whole
page
</h1>
<a href="https://stackoverflow.com/questions/74654088/load-specific-div-with-a-react-component-without-reloading-the-whole-page">
Link to Stackoverflow
</a>
<div style={{ marginTop: "20px" }}>
<button onClick={handleButtonClick}>Magic</button>
</div>
{showComponent ? (
<div style={{ marginTop: "20px" }}>
This is the place of your component!
</div>
) : (
""
)}
</div>
);
}
In the first place I wpuld not use vanilla JS syntax on a react app if it is not necessary. i.e: document.getElementById("datapanel").innerHTML="abc<TranslationsList />".
If you are using React you should be managing the State in the component of the DIV, giving the order to make an element appear once the button is clicked.
A simple example can be this:
CodeSandbox
import { useState } from "react";
export default function App() {
const [divState, setDivState] = useState(null);
const divElement = () => <div>I am the element that should appear</div>;
const handleDiv = () => {
setDivState(divElement);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={handleDiv}>Show DIV</button>
<div>{divState}</div>
</div>
);
}
I agree with the answers given above. Since you are already using React, you should take advantage of its features/functionalities. No need to reinvent the wheel.
However, if you are still interested in how to make your current implementation work. You may use renderToString(), which can be imported from ReactDOMServer. Please refer to the following code snippet as an example.
import { renderToString } from 'react-dom/server'
const TranslationsList = () => {
return <div>TranslationsList Content</div>
}
export default function App() {
const loadTarget = () => {
document.getElementById("datapanel").innerHTML=`abc${renderToString(<TranslationsList />)}`;
}
return (
<div>
<button onClick={loadTarget}>Insert Component</button>
<div id="datapanel">Data Panel Holder</div>
</div>
);
}

How should I fetch payment intent secret for Stripe Elements in my Next.js app?

I am trying to implement Stripe payments in my Next.js app as described in the guide here: https://stripe.com/docs/payments/quickstart
The guide tells me that in order to use Stripe Elements for my checkout form, I need to know payment intent. It says:
Create PaymentIntent as soon as the page loads
The issue is - our website will not have a separate payments page, the payment form will be displayed inside the modal, which is loaded on every page of the website. That means, I would have to fetch the payment intent for any user who ever visits any page on our website, whether they're planning to purchase the course or not, just so that I could display the payment form inside the modal. That doesn't seem right to me.
Can you give me some advice, let me know if there's a better way to handle this?
Another issue is that this guide tells me that I should pass the fetched payment intent clientSecret as an option to <Elements/> wrapper.
And if I hover on <Elements/> wrapper in my VSCdoe, it tells me:
[...] Render an Elements provider at the root of your React app so that it is available everywhere you need it. [...]
So, does that mean I have to put <Elements/> wrapper into my _app.tsx file? And that means I'd have to fetch the payment intent clientSecret inside of the _app.tsx? So that my app would fetch payment intent secret any time any user ever loads any page on my website?
Again, this seems pretty weird, wouldn't it slow things down, add extra requests and loading time to all my pages, and create a whole bunch of payment intents that are never used?
Render the payment form in a modal in Layout.js and wrap the
entire project in the Layout component
place this code in _app.js
import React, { useEffect, useState } from "react"
import { loadStripe } from "#stripe/stripe-js"
import { Elements } from "#stripe/react-stripe-js"
import Layout from "../components/Layout"
import PaymentModalForm from "../components/PaymentModalForm"
const promise = loadStripe("pk_test_....")
// replace pk_test_... with your publishable key
const API_URL = "http://localhost:8000"
// replace API_URL with your backend server url
const App = ({ Component, pageProps }) => {
const [secret, setSecret] = useState(null)
useEffect(() => {
const fetchSecret = async () => {
const response = await fetch(`${API_URL}/create_intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [{ id: 'adidas boost', quantity: 2}]
})
})
const { client_secret } = await response.json()
setSecret(clientSecret)
}
fetchSecret()
}, [])
const options = {
clientSecret: secret,
appearance: { theme: "stripe"}
}
return (
{secret && (
<Elements stripe={promise} options={options}>
<Layout>
<Component {...pageProps} />
</Layout>
</Elements>
)}
)
}
export default App
Then in your Layout.js, fill in this code
import PaymentModalForm from "../components/PaymentModalForm"
import React, { useEffect, useState } from "react"
const Layout = ({ children }) => {
const [showModal, setShowModal] = useState(false)
const handleClick = () => {
if (showModal) {
setShowModal(false)
} else {
setShowModal(true)
}
}
return (
<div>
<div className="container">
{children}
<button onClick={handleClick}>Show Payment Modal</button>
</div>
{showModal ? (
<div className="modal fade">
<div className="modal-dialog">
<div className="modal-content">
<PaymentModalForm />
</div>
</div>
</div>
) : ( null )}
</div>
)
}
export default Layout
There's more work to be done in PaymentModalForm.js

Which one is better for pagination, fetchMore or getServerSideProps?

I want to crate a shopping online website.It have product pagination fuction.
In the docs of Nextjs, they recommend use getStaticProps and getStaticPaths than other(fetchMore in getStaticProps), but when I search Nextjs pagination, almost docs or tutorial use getServerSideProps or getInitialProps.
i tried to use with fetchMore and i don't see any problem,so why they use getServerSideProps?
My code
let paginationOptions: PaginationOptionsInput = {
skip: 0,
type: "SALES_DESC",
};
const Index = () => {
const [filterChecked,setFilterChecked] = useState("SALES_DESC")
const [currentPage,setCurrentPage] = useState(1)
const {data,fetchMore,loading} = useGetProductsQuery({
variables:{
paginationOptions
},
notifyOnNetworkStatusChange: true
})
console.log(data?.getProducts.products)
const handlePageChange = (page:number) =>{
setCurrentPage(page)
paginationOptions.skip = 4 * (page-1)
fetchMore({
variables:paginationOptions
})
}
return (
<div>
<Navbar />
<div className="distance">
...
{data?.getProducts.products &&
data.getProducts.products!.map((product) => (
<div className="col l-3 m-4 c-6" key={product.id}>
<div className={styles.productItem}>
<img src={product.thumbnail} />
<h2>{product.productName}</h2>
<h3>{product.priceToDisplay} VND</h3>
<div className={styles.paidInfo}>
<h4>{product.sales}</h4>
<h4>{product.commentAmount}</h4>
</div>
</div>
</div>
))}
<Pagination
className="pagination-bar"
currentPage={currentPage}
totalCount={data?.getProducts.totalCount!}
pageSize={data?.getProducts.pageSize!}
onPageChange={(page : number) => handlePageChange(page)}
/>
...
</div>
<Footer />
</div>
);
};
export const getStaticProps: GetStaticProps = async () => {
const { data,error,loading } = await client.query<GetProductsQuery>({
query: GetProductsDocument,
variables: { paginationOptions },
notifyOnNetworkStatusChange: true
});
return {
props: {
paginationProducts: data.getProducts,
},
};
};
export default Index;
Maybe I am missing something, but:
getInitialProps - to forget in nextjs v11+
getServerSideProps - every time page is called, [server-side]
getStaticProps and getStaticPaths - runs during build, [server-side]
I recommend you to take a look at this library [client-side]
When to use getServerSideProps(1) vs getStaticProps (2)
When you have pages where data needs to be loaded every time (example: settings, clients, etc).
When you have a lot of pages that need to be rendered, to reach huge performance. (example: product pages, some text pages - for example, "terms and conditions" with data from DB) where you know links previously. It's why in each getStaticProps page we need getStaticPaths.
SEO optimized pages are getStaticProps
So, if you have some rules for the pages, for example /product/1 /product/2 ... /product/n you need to create page with getStaticProps
Also, take a look at this

Nextjs how to not unmount previous page when going to next page (to keep state)

we are using Nextjs in our web app.
We want to keep stack of pages where users visit to keep state of component on back navigation.
How should we do that?
I have tried https://github.com/exogen/next-modal-pages, but it calls getInitialProps of previous pages again on back.
Here's my solution with a custom _app.js
import React, { useRef, useEffect, memo } from 'react'
import { useRouter } from 'next/router'
const ROUTES_TO_RETAIN = ['/dashboard', '/top', '/recent', 'my-posts']
const App = ({ Component, pageProps }) => {
const router = useRouter()
const retainedComponents = useRef({})
const isRetainableRoute = ROUTES_TO_RETAIN.includes(router.asPath)
// Add Component to retainedComponents if we haven't got it already
if (isRetainableRoute && !retainedComponents.current[router.asPath]) {
const MemoComponent = memo(Component)
retainedComponents.current[router.asPath] = {
component: <MemoComponent {...pageProps} />,
scrollPos: 0
}
}
// Save the scroll position of current page before leaving
const handleRouteChangeStart = url => {
if (isRetainableRoute) {
retainedComponents.current[router.asPath].scrollPos = window.scrollY
}
}
// Save scroll position - requires an up-to-date router.asPath
useEffect(() => {
router.events.on('routeChangeStart', handleRouteChangeStart)
return () => {
router.events.off('routeChangeStart', handleRouteChangeStart)
}
}, [router.asPath])
// Scroll to the saved position when we load a retained component
useEffect(() => {
if (isRetainableRoute) {
window.scrollTo(0, retainedComponents.current[router.asPath].scrollPos)
}
}, [Component, pageProps])
return (
<div>
<div style={{ display: isRetainableRoute ? 'block' : 'none' }}>
{Object.entries(retainedComponents.current).map(([path, c]) => (
<div
key={path}
style={{ display: router.asPath === path ? 'block' : 'none' }}
>
{c.component}
</div>
))}
</div>
{!isRetainableRoute && <Component {...pageProps} />}
</div>
)
}
export default App
Gist - https://gist.github.com/GusRuss89/df05ea25310043fc38a5e2ba3cb0c016
You can't "save the state of the page by not un-mounting it" but you can save the state of your app in _app.js file, and the rebuild the previous page from it.
Check the redux example from next's repo.

Resources