How to properly load global variable with async values(Reactjs)? - javascript

I'm trying to solve this problem that I can't seem to solve with stripe's API's
So when creating a charge with their new version API they say that in the front end we should call
loadStripe('publishable Key',{'Connected account ID'})
and set that to a const.
now I dont undestand how are we supposed to get the ID that is stored somewhere say a database?
As a reference please look at this and here (In Step 3 ...).
What I'm currently doing is something like this
import React from "react";
import ReactDOM from "react-dom";
import { Elements } from "#stripe/react-stripe-js";
import { loadStripe } from "#stripe/stripe-js";
import CheckoutForm from "./CheckoutForm";
//btw I have set this to const and to let and none work
const stripePromise = fetch("url", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
anything: window.sessionStorage.getItem("Variable Account")
//here store something that will tell what account to pull ID data from
})
})
.then(data => data.json())
.then(result => {
return loadStripe("KEY", { stripeAccount: result });
});
class App extends React.Component {
render() {
return (
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
);
}
}
export default App;
but the const seems to not load correctly if one goes with the regular flow of the app say from
myapp.com/home
-> click
myapp.com/home/something
-> then
myapp.com/home/something/payment
stripe is not loading but one refreshes the browser now works but that tells me I'm doing maybe something wrong or I have to make the app refresh in 'componentDidMount()' maybe?
One can set it to be static but connected accounts can be many so if anyone can help me with this I would appreciate it

Generally, you'd want to have this account ID available in your app. But if you need to retrieve it, that's fine, but make sure the stripePromise is what you think it is. For example, I can make this work here with a simulated fetch call here: https://codesandbox.io/s/stripe-connect-w-resolve-wts34
Note that I'm managing the Promise explicitly:
const stripePromise = new Promise((resolve, reject) => {
fetch(...)
.then(data => data.json())
.then(result => {
resolve(
loadStripe(STRIPE_PUBKEY, { stripeAccount: "acct_xxx" })
);
});
});
The fact that you describe this breaking with navigation suggests you might be routing incorrectly. If this is a single page app, the navigation shouldn't cause the App component to re-render.

Related

Router.push is returning my objects as undefined, the array length is correct but the value is "" in Next.js [duplicate]

I got a problem with my dynamic route. It look like this
[lang]/abc
I am trying to get query value from [lang] but when I using useRouter/withRouter i got query during 2-3 render of page ( on first i got query.lang = undefined ). its possible to get in 1 render or use any technique ?
I found something:
isReady: boolean - Whether the router fields are updated client-side and ready for use. Should only be used inside of useEffect methods and not for conditionally rendering on the server.
https://nextjs.org/docs/api-reference/next/router#router-object
And the code would be like:
const router = useRouter();
useEffect(()=>{
if(!router.isReady) return;
// codes using router.query
}, [router.isReady]);
It's impossible to get the query value during the initial render.
Statically optimized pages are hydrated without the route parameters, so the query is an empty object ({}).
Next.js will populate the query after the page has been hydrated.
Next.js 10.0.5 and up
To determine if the route params are ready, you can use router.isReady inside a useEffect hook. For an example, see the answer provided by #doxylee.
Before Next.js 10.0.5
At first render of a dynamic route router.asPath and router.route are equal. Once query object is available, router.asPath reflects it.
You can rely on the query value within a useEffect hook after asPath has been changed.
const router = useRouter();
useEffect(() => {
if (router.asPath !== router.route) {
// router.query.lang is defined
}
}, [router])
GitHub Issue - Add a "ready" to Router returned by "useRouter"
In NextJS 9+, one way to ensure route parameters are immediately available for page components is to get them from the context arg passed to getServerSideProps() and pass to the component as props.
For a page like [id].js,
export function getServerSideProps(context) {
return {
props: {params: context.params}
};
}
export default ({params}) => {
const {id} = params;
return <div>You opened page with {id}</div>;
};
This is a great question and one that took a few days for me to figure out what the best approach is.
I have personally found three viable solutions to the problem of validating dynamic route path params or even just route path params in general.
The three solutions are
SSR (don't recommend) [Next >= 10]
useRouter
Middleware [Next 12 required]
In my examples a will use a route that requires a reset-token or it should be redirected.
SSR
Firstly server side rending with getServerSideProps.
Vercel recommends to use SSR as a last resort and I would highly recommend not using SSR when able (time to byte & cost).
We suggest trying Incremental Static Generation or Client-side Fetching and see if they fit your needs.
https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation
But in the case that you do, say there is some server side api validation call you require to validate the query param.
export const getServerSideProps = async (context) => {
const { token } = context.query;
if (!token) {
return {
redirect: {
permanent: false,
destination: "/",
}
}
}
return {
props: {}
// props: { token }
// You could do this either with useRouter or passing props
}
}
useRouter Secondly the easiest useRouter. When I first did this I came across the problem when nextjs/react hydrates there will be a point when the query params are null. Luckily useRouter has isReady!
import Router, { useRouter } from "next/router";
const { query, isReady } = useRouter();
useEffect(() => {
if (!isReady) return;
if (!query.token) {
Router.push("/")
}
}, [isReady])
Middleware now this is my personal favourite as it seperates the functionality in a clean way imo.
I found this based of a vercel example. I would highly recommend reading through a bunch of these to find best practices.
https://github.com/vercel/examples/
import { NextResponse, NextRequest } from 'next/server'
export async function middleware(req) {
const { pathname, searchParams } = req.nextUrl
if (pathname == '/reset-token') {
const index = searchParams.findIndex(x => x.key === "token")
// You could also add token validation here.
if (!index) {
return NextResponse.redirect('/')
}
}
return NextResponse.next()
}
Here is the repo which has some cool filtering of query parameters.
This is a more soft approach instead of hard redirecting.
https://github.com/vercel/examples/tree/main/edge-functions/query-params-filter
Nico also has a great answer on this, expect I wouldn't recommend using hooks like in his example, instead use isReady.
https://stackoverflow.com/a/58182678/4918639
For Class Component Lovers
The even better approach is to listen for a dedicated event for this routeChangeComplete using this.props.router.events.on method, inside componentDidMount if you're using class component -
routeChangeComplete = () => {
// this WILL have valid query data not empty {}
console.log(this.props.router.query);
};
componentDidMount() {
this.props.router.events.on("routeChangeComplete", this.routeChangeComplete);
}
componentWillUnmount() {
this.props.router.events.off("routeChangeComplete", this.routeChangeComplete);
}
Ref: https://nextjs.org/docs/api-reference/next/router#routerevents
routeChangeComplete: Fires when a route changed completely.
Practically when isReady has become true or when router.query object has data.
For NextJS version - 12.0.8
"If you export a function called getServerSideProps (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps."
=async functions
refference:https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#getserversideprops
Simply putting that async function on the page notifies NextJS of its presence.During prerendering stage of the component, the query object of the router will be empty.
isReady: boolean - Whether the router fields are updated client-side and ready for use. Should only be used inside of useEffect methods and not for conditionally rendering on the server.
refference: https://nextjs.org/docs/api-reference/next/router
solution:
import { useRouter } from 'next/router';
const Fn = () =>{
const router = useRouter();
const { param } = router.query;
const fetchData = async () => {
await fetch();
}
useEffect(() => {
fetchCat();
}, [router.isReady]);
}
I resolved my problem that I need it in Hoc component.
I wrapped using withRouter(withLocale(Comp)) and create conditional in HOC
export default function withLocale(WrappedPage) {
const WithLocale = ({ router, ...props }) => {
const { lang } = router.query;
if (!lang || !isLocale(lang)) {
return <Error statusCode={404} />;
}
return (
<LocaleProvider lang={lang}>
<WrappedPage {...props} />
</LocaleProvider>
);
};
return WithLocale;
}
Next.js <= 10.0.5
This is a good work around, I found around from this comment
Add useQuery.ts helper file
// useQuery.js
import { useRouter } from 'next/router';
// Resolves query or returns null
export default function useQuery() {
const router = useRouter();
const ready = router.asPath !== router.route;
if (!ready) return null;
return router.query;
}
usage
// In your components (instead of useRouter)
const query = useQuery();
useEffect(() => {
if (!query) {
return;
}
console.log('my query exists!!', query);
}, [query]);
Class Component | 12/16/2022 | React JS 18.2.0 | Next JS 13.0.6
I got the answer for those who want to use Class Component. This was actually nowhere to be found ! Enjoy !
You will add if(this.props.router.isReady) and include return in the condition in render().
.
.
import { withRouter } from 'next/router';
import { Component } from 'react';
class User extends Component {
...
render() {
if(this.props.router.isReady){ // Add this condition and include return ()
// Do anything
console.log(this.props.router.query) // Log 'query' on first render
return (
<div>
<SearchBar pid={this.props.router.query.pid} /> // Pass the query params to another component if needed
</div>
);
}
}
}
export default withRouter(User);

Why does my React app not re-render or display changes on the DOM except I relaod?

import { useEffect, useState } from "react";
function Popular() {
const [popular, setPopular] = useState([]);
useEffect(() => {
getPopular();
}, []);
const getPopular = async () => {
const api = await fetch(
`https://api.spoonacular.com/recipes/random?apiKey=${process.env.REACT_APP_RECIPE_API_KEY}&number=9`
);
const data = await api.json();
setPopular(data.recipes);
};
return (
<div>
{popular.map((recipe) => {
return (
<div>
<p>{recipe.title}</p>
</div>
);
})}
</div>
);
}
export default Popular;
I am pretty new to React, and I encountered this issue which I have been trying to fix to no avail. The code is a component that is to return a list of recipe title to my app. I am fetching data from an API in the getPopular() function which is set to the setPopular function variable of the useState() method. But when I save my work and return to the browser, the changes does not display. The list does not display, but if I console.log(data.recipes) it displays on the console.
Before now, if I made any change (maybe a text change) the React app renders it without reloading, but now I have to reload the page before I see the change.
Please how do I fix this issue? So that I can see changes without having to reload the page manually.
Not saying that this is the problem, but getPopular() should not be called after its declaration? By this I mean:
const getPopular = async () => {
const api = await fetch(
/...
};
useEffect(() => {
getPopular();
}, []);
Another thing that bugs me is, although JS/React is case sensitive, I really think you should avoid having a const called popular, since your functions is Popular.
Please, let me know if the order did matter for your problem. I will review some react classes soon, if i get another inside, i'll let you know.

Gatsby 3rd party scripts not working as intended

I have the problem with including fastspring to my gatsby project. Problem is the following: I add the script in html head but it doesn't work on all pages(it works only if I refresh the page)
I tried to fix that by inserting script in html.js, with gatsby-ssr.js and gatsby-browser.js
gatsby-browser.js
I put the same code in gatsby-ssr.js, I have also tried with Helmet but nothing works for me
I want it to work on all the pages without needing to refresh the page, so if somebody could help me with this. Thanks in advance! :)
Seems like an old issue, but someone might still have the problem.
I did search for a solution to it for some time and in the end, I went with the following.
I created a component called FastSpringStoreApi.js. It loads FastSpring API and subscribes to 2 callback events from it - data-popup-closed and data-data-callback. I used this two to dispatch custom js events for which I listen in my FastSpring provider. These 2 events contain all information needed for store to function (items, pricing, cart information)
Note: there is a reason I save data into sessionStorage. The event can be dispatched before React hydrates. And in cases like this I take data in session storage as initial state in my reducers.
import React from 'react';
import {
FS_EVENTS,
FS_SESSION_KEY
} from './FastSpringStore.keys';
export default ({ storeFrontId }) => (
<>
<script
type="text/javascript"
dangerouslySetInnerHTML= {{
__html:`
function raiseFSPopupCloseEvent(data) {
var popupCloseEvent = new CustomEvent(
'${FS_EVENTS.POPUP_CLOSE}',
{ detail: data }
);
window.dispatchEvent(popupCloseEvent);
}
function raiseFSDataUpdateEvent(data) {
var dataUpdateEvent = new CustomEvent(
'${FS_EVENTS.DATA_UPDATE}',
{ detail: data }
);
window
.sessionStorage
.setItem(
'${FS_SESSION_KEY}',
JSON.stringify(data)
)
window.dispatchEvent(dataUpdateEvent);
}
`
}}
/>
<script
id="fsc-api"
src="https://d1f8f9xcsvx3ha.cloudfront.net/sbl/0.8.5/fastspring-builder.min.js"
type="text/javascript"
data-popup-closed="raiseFSPopupCloseEvent"
data-data-callback="raiseFSDataUpdateEvent"
data-continuous="true"
data-storefront={storeFrontId}
/>
</>
)
I load this component inside gatsby-ssr.js only
export const onRenderBody = ({ setHeadComponents }) => {
setHeadComponents([
<FastSpringStoreApi
key="fast-spring-store-api"
storeFrontId="..."
/>,
])
}
I have FasSpringStore provider where I subscribe to my fs events. Looks like this. With it I can get all data needed further down to any of the components.
useEffect(() => {
const onPopupClosed = (data) => {
// Popup was closed and order was finished (we have order id)
if (_has(data.detail, 'id')) {
// Navigate to home page
// TODO: Show thank you page in the future
navigate('/')
dispatch(cleanShopStore())
}
}
const onDataUpdate = (data) => {
dispatch(
setOrderInfo(
mapFSDataToPayload(
data.detail
)
)
)
}
window.addEventListener(FS_EVENTS.POPUP_CLOSE, onPopupClosed, false);
window.addEventListener(FS_EVENTS.DATA_UPDATE, onDataUpdate, false);
return () => {
window.removeEventListener(FS_EVENTS.POPUP_CLOSE, onPopupClosed)
window.removeEventListener(FS_EVENTS.DATA_UPDATE, onDataUpdate)
window.sessionStorage.removeItem(FS_SESSION_KEY)
}
}, [])
Inside gatsby-ssr.js I wrap my root element with store provider
export const wrapRootElement = ({ element }) => (
<FastSpringStoreProvider>
{element}
</FastSpringStoreProvider>
);
Same goes for gatsby-browser.js
export const wrapRootElement = ({ element }) => (
<FastSpringStoreProvider>
{element}
</FastSpringStoreProvider>
);
Hope this gives some ideas for FastSpring implementation.

how to display data in table using json in react js?

i have JSON like this
i want to use this JSON and display data in Table using react js.
this is how i display data from JSON file.
import React, { Component } from 'react';
import data from './data.json';
class App extends Component {
render() {
return (
<ul>
{
data.map(function(movie){
return <li>{movie.id} - {movie.title}</li>;
})
}
</ul>
);
}
}
export default App;
how to load JSON from URL and display it in table using reactjs?
You could fetch the JSON once the component will mount, and when you eventually resolve it you can update the state of the component:
import React, { Component } from 'react';
class App extends Component {
// initially data is empty in state
state = { data: [] };
componentDidMount() {
// when component mounted, start a GET request
// to specified URL
fetch(URL_TO_FETCH)
// when we get a response map the body to json
.then(response => response.json())
// and update the state data to said json
.then(data => this.setState({ data }));
}
render() {
return (
<ul>
{
this.state.data.map(function(movie){
return <li key={movie.id}>{movie.id} - {movie.title}</li>;
})
}
</ul>
);
}
}
export default App;
If you're unable to use fetch, you could use some other libraries like superagent or axios. Or you could even fall back to good ol' XMLHttpRequest.
On another note, when building a list of component it is important they each child have a unique key attribute. I also updated that in the code, with the assumption that movie.id is
Example axios code:
axios.get(URL)
.then(response => response.data)
.then(data => this.setState({ data }));
EDIT: as trixn wrote in a reply, componentDidMount is the preferred place to fetch data. Updated code.
EDIT 2: Added axios code.
You can use axios to send http requests.
It looks like this :
const response = await axios.get(your_url_here);
const items = response.data.items;
About await keyword : How to use await key word on react native?
This is axios GitHub page for the docs : https://github.com/axios/axios
Hope it helps.
You can use the fixed Data table to display the data from the json Response.Since the data is vast and it would be difficult to manage the conventional table, this would be a better alternative.
The documentation is given at
https://github.com/schrodinger/fixed-data-table-2/blob/master/examples/ObjectDataExample.js
This link will help you.

Relay Modern isn't sending network request to GraphQL Server

I just started looking at Relay Modern recently and creating a simple app with a GraphQL backend (which works perfectly fine when testing with GraphIQL). However, I'm running into problems with Relay not sending network requests to retrieve any data. I'm not 100% confident about the below code but I definitely would expect it to at least send a network request to http://localhost:3000/graphql, but the devtools don't show any such request (or server logs).
environment.js
import { Environment, Network, RecordSource, Store } from 'relay-runtime';
const store = new Store(new RecordSource());
const network = Network.create((operation, variables) =>
fetch('http://localhost:3000/graphql', {
method: 'POST',
headers: {
// Add authentication and other headers here
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}).then(res => res.json()),
})
);
const environment = new Environment({
network,
store,
});
export default environment;
App.jsx
import React, { Component } from 'react';
import { graphql, QueryRenderer } from 'react-relay';
import environment from '#utilities/environment';
class App extends Component {
render() {
console.log(this.props); // Empty object {} here
return (
<div>
Hello World!
</div>
);
}
}
const Query = graphql`
query AppQuery {
user(id: "u01") {
id
username
}
}
`;
const AppQuery = () =>
(<QueryRenderer
environment={environment}
graphql={Query}
variables={{}}
render={({ error, props }) => {
console.log(error, props); // Get (undefined, {}) here
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return <App {...props} />;
}
return <div>Loading!</div>;
}}
/>);
export default AppQuery;
Am I missing something obvious? There are no console/webpack errors and the app renders properly, such as it is, but simply no GraphQL/Relay data. Thanks!
I think your environnement is just fine.
Few things that might help : You might want to create a FragmentContainer and setup/run Relay Compiler to generate the needed graphql files in order to Relay run your queries.
You probably want declare and collocate the data requirements with App through a FragmentContainer. You need a Fragment Container because your data is masked in App hence not available through props (see more here why it's masked).
You'll need to use createFragmentContainer() like this :
App = createFragmentContainer(
App,
graphql`
fragment App_users on User { // The fragment name should follow the convention <FileName>_<propName>, so maybe you might the App component to an another file.
user(id: "u01") {
id
username
}
}
`,
);
Modify the Query to :
graphql`
viewer {
...App_users_viewer
}
`
When done, you should be able to run the Relay Compiler and have graphql generated files

Categories