How to call function after URLSearchParam? - javascript

I need redirect page after it load and get param from URL. I can do by button click. But how I can make page redirect automatic after page have get param from URL (without user input)?
Thanks!
const handleClick = async (event) => {
const stripe = await stripePromise;
const { error } = await stripe.redirectToCheckout({
param,
});
}
const param = new URLSearchParams(window.location.search).get('param');
const Page = () => {
React.useEffect(() => {
window.scrollTo(0, 0);
}, []);
return (
<section >
<button role="link" onClick={handleClick}>
Press
</button>
</section>
);
};
export default Page;

You could probably just use your useEffect hook there to redirect the page, instead of using the onClick handler. e.g.
const handleLoad = async () => {
const stripe = await stripePromise;
const { error } = await stripe.redirectToCheckout({
param,
});
}
const param = new URLSearchParams(window.location.search).get('param');
const Page = () => {
React.useEffect(() => {
handleLoad();
}, []);
return null;
};
export default Page;
Though, you don't really even need to load up React for any of this. You could just make this page a static html page with a script tag that does the redirect logic, or something similar to that.
After further discussion, we found out that in order for this stripe stuff to work on Safari-mobile, we needed to wait for the page's load event before calling handleLoad(), so something like this:
const handleLoad = async () => {
const stripe = await stripePromise;
const { error } = await stripe.redirectToCheckout({
param,
});
}
const param = new URLSearchParams(window.location.search).get('param');
window.addEventListener('load', () => handleLoad())

You're going to want to parse those query parameters in the init/load handler. You can see an example of this here. Note that the event listener on DOMContentLoaded kicks everything off. An example of this in action can be seen here.
I noticed that your code uses param in the Checkout redirect directly. If param is a checkout session that you should be generating these on demand, not sending them in emailed links etc, as they are short-lived. In the example above, the code param is read and use to generate a new checkout session via a fetch and then that is used for the redirect.

Related

Testing with third party dependencies

if in js I have this function (don't focus on what this codex does):
export const user = () => {
const user = ref(null)
const load = async () => {
const _user = await getCurrentUser()
const userRef = doc(collection(db, 'users'), _user?.uid)
await useDocument(userRef, { target: user }).promise.value
}
return {
user,
load
}
})
how do i test user.load()? This function contains dependencies like getCurrentUser() and useFireStore() which must be configured before calling the user function itself.
Basically, how do I manage these external dependencies? i know i should use mocks but i don't understand how they work.

Sveltekit Error: `page` in `load` functions has been replaced by `url` and `params`

I am trying to display my date from GraphCMS in my blog application. I receive this error when I go to my single post link (http://localhost:3000/posts/union-types-and-sortable-relations)
"
page in load functions has been replaced by url and params
Error: page in load functions has been replaced by url and params
"
Here is my code
<script context='module'>
export const load = async ({fetch, page: {params}}) => {
const {slug} = params
const res = await fetch(`/posts/${slug}.json`)
if(res.ok) {
const {post} = await res.json()
return {
props: {post},
}
}
}
</script>
<script>
export let post
</script>
<svelte:head>
<title>Emrah's Blog | Welcome</title>
</svelte:head>
<pre>{JSON.stringify(post, null, 2)}</pre>
Can you please help. Thanks
Try using params instead of page: params, though the latter still works in Sveltekit 278 (which I'm using).
Besides, I'm curious to know what makes you prefer this method to querying GraphCMS for your single post. I do it like this:
import {client} from '$lib/js/graphql-client'
import {projectQuery} from '$lib/js/graphql-queries'
export const load = async ({ params }) => {
const {slug} = params
const variables = {slug}
const {project} = await client.request(projectQuery, variables)
return {
props: {
project
}
}
}
Yes, this has been changed a while ago, now the different parts of what used to be page are passed directly into the load function:
export async function load({ fetch, page }) {
const { params, url } = page
}
export async function load({ fetch, params, url }) {
}
Something else to consider is that now there are page endpoints, if your file is [slug].svelte you can make a file [slug].js and add the following:
export async function get({ params }) {
const { slug } = params;
const post = {}; // add the code to fetch from GraphCMS here
return {
status: 200,
body: {
post
}
}
}
With this you can remove the load function and make your code simpler (especially because you technically already have all this code in your /posts/[slug].json.js file.
<script context='module'>
export async function load({ fetch, params}){
let id = params.users
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
const user = await response.json()
if(response.ok){
return {props:{user}}
}
return {
status: response.status,
error : new Error(" sorry no user found")
}
}
export let user

How to share a dynamically generated image from react with react-share?

I have a react app which generates images on the front end dynamically using Plotly.js. I'd like to add image sharing functionality. I am trying to use react-share for this. Social platforms require image URL for image sharing and do not support images in base64 encoding or alike. Backend was implemented so it can receive images in base64, store in the database and return URL to the image, which is then used for sharing with react-share.
As the image is generated dynamically (it changes each time user resizes the chart, for instance), everything should be done when user clicks on Share icon.
So after the user has clicked on the Share icon, the image generated on the front end should be saved to back end
let imgURI;
const handleClick = () => {
Plotly.toImage('chartContainer', {
format: 'png',
width: 1000,
height: 600
})
.then(dataUrl => api.post('/image/base64ToPng', { image: dataUrl })
.then(
(response) => {
imgURI = response.data.imgURI;
},
failure => console.error(failure)
));
};
after the response is received, passed down to the sharing component like this
<FacebookShareButton
url={imgURI}
>
<FacebookIcon/>
</FacebookShareButton>
The code sample is not asynchronous, so the image URI is not passed to the sharing component, therefore sharing does not work. I tried to pass the prop down using conditional depending on whether it's defined or not and did not come up with a solution. I also looked up some issues in react-share repo that dealt with async urls, but seems like none of them deals with the dynamic image sharing on click.
I'd very appreciate a hint on how to complete this task.
This is serious hack territory, and the whole thing would be a lot simpler if this PR had been completed.
However, the code below should work (see codesandbox).
The key steps are:
Have a bit of state that keeps track of whether you have a url from the service or not.
When this state is "none", disable the facebook button's default behavior (i.e. openShareDialogOnClick = false)
Add an onClick handler to the facebook button that asynchronously fetches the url and sets the state (triggering a re-render)
Use an effect + ref so that when the url is set to something real, you manually call the click event on the button (which now has a real address in its url prop), and then re-sets the url to "none"
import { useEffect, useRef, useState } from "react";
import { FacebookIcon, FacebookShareButton } from "react-share";
async function getUrFromService(): Promise<string> {
// The real implementation would make a network call here.
await new Promise((resolve) => setTimeout(resolve, 1000));
return "https://via.placeholder.com/150";
}
export default function App() {
const shareButton = useRef<HTMLButtonElement>(null);
const [url, setUrl] = useState<string>("none"); // Unfortunately, we have to have a dummy string here, or FacebookShareButton will blow up.
// Provide an onClick handler that asyncronously fetches the url and sets it in the state.
const onClick = async () => {
// Be sure to check for the "none" state, so we don't trigger an infinite loop.
if (url === "none") {
const newUrl = await getUrFromService();
setUrl(newUrl);
}
};
// Whenever "url" changes and we re-render, we manually fire the click event on the button, and then re-set the url.
useEffect(() => {
if (url !== "none") {
shareButton.current?.click();
setUrl("none");
}
}, [url, shareButton]);
return (
<FacebookShareButton
ref={shareButton}
// Disable calling the dialog if we don't have a url yet.
openShareDialogOnClick={url !== "none"}
url={url}
onClick={onClick}
>
<FacebookIcon />
</FacebookShareButton>
);
}
Use navigator.share api if possible.
Having, said that you could write a wrapper that manages loading state and disables icon of component.
Create a async share component
//AsyncShareLoader.jsx
const AsyncShareLoader = ({ url, children }) => {
const loading = !url;
return (
<div style={{ filter: `grayscale(${loading ? "100%" : "0%"}` }}>
{React.Children.map(children, (child) =>
React.cloneElement(child, {
disabled: loading,
url: loading ? "none" : url,
openShareDialogOnClick: !loading
})
)}
</div>
);
};
Now, use this as a wrapper for your actual react-share icons. Something like this
const Share = () => {
const [url, setUrl] = useState()
useEffect(() => {
fetch('/imgUrl').then(getUrlFromRes).then(setUrl)
}, [])
return (
<AsyncShareLoader url={url}>
<FacebookShareButton>
<FacebookIcon />
</FacebookShareButton>
</AsyncShareLoader>
);
}
Extending this techinique you could manually steal click as Andrew suggested
const getBase64ImageFromUrl = url =>
fetch(url)
.then(response => response.blob())
.then(
blob =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
blob = new Blob([blob], {type: 'image/png'});
reader.readAsDataURL(blob);
}),
);
const shareData = async () => {
await getBase64ImageFromUrl('https://url of image').then(base64 => {
const shareOption = {
title: App Link,
url: base64,
message:
'Download app now: ,AppLink:""',
};
try {
const shareResponse = Share.open(shareOption);
console.log('share response ,', shareResponse);
} catch (error) {
alert(error.message);
}
});
};

How to re-render react component depends on a file?

I have a file that stores an array of objects. I have a component that fetches data from this file then render the list. The file could be updated somewhere else, I need the component to be updated if the file is modified. I have following code example
const header = () => {
const [list, setList] = useState([]);
// fetch
useEffect(() => {
const loadList = async () => {
const tempList = await getList("/getlist"); // get call to fetch data from file
setList(tempList);
};
loadList ();
}, [list]);
// function to render content
const renderList = () => {
return list.map(obj => (
<div key={obj.name}>
{obj.name}
</div>
));
};
return (
<div>{renderList()}</div>
)
}
// get call
router.get('/getlist',
asyncWrapper(async (req, res) => {
const result = await getList();
res.status(200).json(result).end();
})
);
const getList= async () => {
const list = JSON.parse(await fs.readFile(listPath));
return list;
}
Code has been simplified. If I remove the list from useEffect, then it will only render once and will never update unless I refresh the page. If I include list there, loadList() will get called constantly, and component will get re-rendered again and again. This is not the behavior I want. I am just wondering without making header component async component, how do I only re-render this component when the file is changed?
Thank you very much.
There are two approaches you can take to this:
Polling
Request the URL on an interval, and clear it when the component is unmounted.
Replace loadList () with:
const interval = setInterval(loadList, 60000); // Adjust interval as desired
return () => clearInterval(interval)
Make sure the cache control headers set in the response to /getlist don't stop the browser from noticing updates.
Server push
Rip out your current code to get the data and replace it with something using websockets, possibly via Socket.IO. (There are plenty of tutorials for using Socket.io with React that can be found with Google, but its rather too involved to be part of a SO answer).

Async and await callout to firebase is not waiting

I have a dating-like app and I want to be able to query for new matches every time a user clicks on the "connection" tab button.
I am not sure if I am writing the await or async incorrectly but if the user moves too fast for the database to return the results, the returned matches are not loaded fast enough. What I have so far is: on the load of the page I callout to Firebase, when the user navigates away and then navigates back to the "connection" tab, I call back out to Firebase. the getMatches() method is the callout to firebase.
const MatchesScreen = ({navigation}) => {
const {state, updateDislikedQueue, updateLikedQueue, getMatches} = useContext(AuthContext);
const [loaded, setLoaded] = useState(false);
const [queue, setQueue] = useState({});
const [noMatches, setNoMatches] = useState(false);
const [updateProfileAndPreferences,setUpdateProfileAndPreferences] = useState(false);
const getMatchesMethod = async () => {
getMatches().then(matches => {
if (!matches) {
Alert.alert("Update Preferences and Profile before connecting");
setUpdateProfileAndPreferences(true);
} else {
setUpdateProfileAndPreferences(false);
let cardData = [];
for (m in matches) {
if (matches[m].id == state.id) {
continue;
} else {
let user = {
id: matches[m].id,
fullName: matches[m].info.fullName
};
cardData.push(user);
}
}
if (cardData.length > 0) {
setQueue(cardData);
setLoaded(true);
} else {
setNoMatches(true);
Alert.alert("No Connections Available");
}
}
});
};
useEffect(() => {
getMatchesMethod();
const unsubcribe = navigation.addListener("willFocus", () => {
getMatchesMethod();
});
// return unsubcribe.remove();
}, []);
Also when I try to unsubscribe, the listener doesn't appear to work when the user navigates back and forth. Any help on what I am doing wrong with the async calls and the listener would be greatly appreciated.
I think you just forgot the await keyword in your function
Happens to me all the time ahaha
I recommend using the await keyword in front of getMatches() instead of the .then() syntax. It makes the code read more synchronously, and it can help prevent errors. Await must always be called from an asynchronous function. It cannot be called from a non async function.
I think you will also need to use an await in front of getMatchesMethod();
Checkout this article for help with calling asynchronous code inside an useEffect()
https://medium.com/javascript-in-plain-english/how-to-use-async-function-in-react-hook-useeffect-typescript-js-6204a788a435
Ex:
const unsubcribe = navigation.addListener("willFocus", async () => {
await getMatchesMethod();
});
I found the issue. I was on V3 of react-navigation which is severely outdated was was conflicting with my react-native and expo versions.
I am in the process of updating everything (RN > .60, EXPO sdk > 35, #react-navigation V5) and will be able to use the listeners for #react-navigation v5 on my getMatches() method.

Categories

Resources