Problem when trying to fetching data with websocket - next.js

I'm trying to fetch Binance real time data with node-binance-api package in my NextJS project, i have know that to let real time data render on my UI, i have to use websocket, at getServerSideProps() function, I dont know how to return the exactly the real time data and render it on BinanceData() function
I use Nextjs, node-binance-api and websocket, websocket is already connected.
Here is what I trying data
import React from 'react'
const Binance = require('node-binance-api')
import {io} from 'socket.io-client'
let socket = io();
const BinanceData = ({price}) => {
const socketInitializer = async () => {
await fetch('/api/socket')
socket = io()
socket.on('connect', () => {
console.log('connected')
})
}
socketInitializer();
return (
<div>
<h2>
{price}
</h2>
</div>
)
}
export default BinanceData
export async function getServerSideProps()
let sockets = io();
const apiKey = process.env.NEXT_PUBLIC_BINANCE_API_KEY;
const secretKey = process.env.NEXT_PUBLIC_BINANCE_SECRET_KEY;
const binance = new Binance().options({
APIKEY: apiKey,
APISECRET: secretKey
});
const price = binance.futuresMiniTickerStream('BTCUSDT', (data) =>{
console.log(data.close);
sockets.emit('send-price',data.close);
});
return {
props:{
price
}
}
}
I already get my real time data in console log with line console.log(data.close); and here is my result
enter image description here
I do not know how to return the correct value of "price" in order to get the correct value on
BinanceData() function and my real time data is appear correct on my UI
I'm very grateful with any advise and I will carefully trying on it.
Thanks for all of you help !

Related

How to use `useRoute`/`useRouter` in a Pinia Store using Setup Store syntax in Vue3?

I've been trying to get my Pinia store up and running in Vue 3 and it all has been pretty effortless until I wanted to access some parameters in the url.
I have a store (simplified) like so:
import { defineStore } from 'pinia';
import { useRoute } from 'vue-router';
import { useLocalStorage } from '#vueuse/core';
export const useUserStore = defineStore('user', () => {
const route = useRoute();
const uuid = ref(
useLocalStorage('uuid', route.params.id)
)
return { uuid };
})
Unfortunately, the route remains undefined as if useRoute() is not triggered properly. I've seen that you can add plugins to add the router instance to the pinia store on initialisation, but there's no way I can find to access that this instance in a Setup Store.
Any help would be greatly appreciated
route is not defined when the pinia is initiated.
You need to wait a bit.
One way to do this is to call the function when the component is loaded.
export const useUserStore = defineStore('user', () => {
const route = useRoute();
const id = ref('');
const setId = () => {
id.value = route.params.id as string; // don't need as string if you don't use TypeScript
};
return { id, setId };
});
<script setup lang="ts">
import { useUserStore } from '../stores/user';
const user = useUserStore();
user.setId(); // call the function from pinia, route.params works just fine
</script>
Link Demo

Uploaded image URL not being stored in firestore the first time I setDoc

I am trying to store the downloadLink from firebase's storage into firestore. I am able to set all the data, and I am able to set the link, the second time I click the "post" button.
I know the issue has to do with asynchronous functions, but I'm not experienced enough to know how to solve the issue.
In the "createPost" function, I am console logging "i am the URL: {url}" and in the "uploadFile" function, I am console logging "look at me {url}" to debug.
I noticed the "I am the URL" outputs nothing and then shortly after, the "look at me" outputs the URL.
setDoc() of course stores the imageLink as an empty string.
What can I do to solve this? Any help would be greatly appreciated or any documentation to help with my understanding of async functions.
Here is my code:
const PostModal = (props) => {
const makeid = (length) => {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const [descriptionText, setDescriptionText] = useState("");
const [addressText, setAddressText] = useState("");
const [venueText, setVenueText] = useState("");
const [startTimeText, setStartTimeText] = useState("");
const [endTimeText, setEndTimeText] = useState("");
const [shareImage, setShareImage] = useState("");
const [videoLink, setVideoLink] = useState("");
const [assetArea, setAssetArea] = useState("");
const [url, setURL] = useState("");
const { data } = useSession();
const storage = getStorage();
const storageRef = ref(storage, `images/${makeid(5) + shareImage.name}`);
const uploadFile = () => {
if (shareImage == null) return;
uploadBytes(storageRef, shareImage).then( (snapshot) => {
//console.log("Image uploaded")
getDownloadURL(snapshot.ref).then( (URL) =>
{
setURL(URL);
console.log(`look at me: ${URL}`)});
});
}
const createPost = async () => {
var idLength = makeid(25);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadFile()
console.log(`I am the URL: ${url} `)
setDoc(doc(db, "posts", idLength), {
eventDescription: descriptionText,
eventAddress: addressText,
venueName: venueText,
startTime: startTimeText,
endTime: endTimeText,
imageLink: url,
videoLink: videoLink,
username: data.user.name,
companyName: !data.user.company ? "" : data.user.company,
timestamp: Timestamp.now(),
});
}
const handleChange = (e) => {
const image = e.target.files[0];
if(image === '' || image === undefined) {
alert('not an image, the file is a ${typeof image}');
return;
}
setShareImage(image);
};
const switchAssetArea = (area) => {
setShareImage("");
setVideoLink("");
setAssetArea(area);
};
const reset = (e) => {
setDescriptionText("");
setAddressText("");
setVenueText("");
setStartTimeText("");
setEndTimeText("");
setShareImage("");
setVideoLink("");
setURL("");
props.handleClick(e);
};
This was taken from a reddit user who solved my answer. Big thank you to him for taking the time to write out a thoughtful response.
So, you're kinda right that your issue has a bit to do with asynchronicity, but it's actually got nothing to do with your functions being async, and everything to do with how useState works.
Suffice it to say, when you call uploadFile in the middle of your createPost function, on the next line the value of url has not yet changed. This would still be true even if uploadFile were synchronous, because when you call a useState setter function, in this case setURL, the getter value url doesn't change until the next time the component renders.
This actually makes perfect sense if you stop thinking about it as a React component for a moment, and imagine that this was just vanilla JavaScript:
someFunction () {
const url = 'https://www.website.com';
console.log(url);
anotherFunction();
yetAnotherFunction();
evenMoreFunction();
console.log(url);
}
In this example, would you ever expect the value of url to change? Probably not, since url is declared as const, which means if the code runs literally at all, it's physically impossible for the value of url to change within a single invocation of someFunction.
Functional components and hooks are the same; in a single "invocation" (render) of a functional component, url will have the same value at every point in your code, and it's not until the entire functional component re-renders that any calls to setURL would take effect.
This is an extremely common misunderstanding; you're not the first and you won't be the last. Usually, it's indicative of a design flaw in your data flow - why are you storing url in a useState to begin with? If you don't need it to persist across distinct, uncoupled events, it's probably better to treat it like a regular JavaScript value.
Since uploadBytes returns a promise, you could make uploadFile asynchronous as well, and ultimately make uploadFile return the information you need back to createPost, like this:
const uploadFile = async () => {
if (shareImage == null) return;
const snapshot = await uploadBytes(storageRef, shareImage);
// console.log("Image uploaded")
const URL = await getDownloadURL(snapshot.ref);
return URL;
};
All I've done here us un-nest your .then calls, pulling the trapped values out into the usable scope of your uploadFile function. Now, you can change that one line of createPost to this:
const url = await uploadFile();
and eliminate your useState altogether.

Promise {<pending>} Graphql, and strapi with nextjs

With GraphQL and nextjs, I'm trying to retrieve some data from strapi.
When I try to access these data from the other file and display them on the UI, I get this error Promise {} in console.log.
This is what i tried
sliderAdapter.js
import { fetchSlider } from "./apiClient";
export const sliderAdapter = async (data, locale, url) => {
const sl = await fetchSlider();
const deepDownSlides = sl.data?.slides?.data;
if (deepDownSlides.length > 0) {
const slider = deepDownSlides[0]?.attributes?.slider;
// console.log("slider", slider);
return slider;
}
// This code is working but not properly, just return the data into the console.
return "";
};
fetchSlider is the file where i put the query.
Next:
import { sliderAdapter } from "../../lib/sliderAdapter";
const Slider = (data) => {
const slide= sliderAdapter(data)
console.log("slide", slide)
If anyone knows or can find the issues, plz let me know :)
Your function is asynchronous so you have to retrieve the value once the promise is resolved
sliderAdapter(data).then(slide=>console.log(slide))

Vue3 - OnMount doesn´t load array

In Vue 3 i need to fill some array with result of store. I import store like this
Imports
import { onMounted, ref, watch } from "vue";
import { useTableStore } from "../stores/table";
Then i declare values and try to fill it
const search = ref(null);
const searchInput = ref("");
const edition = ref([]);
const compilation = ref([]);
const debug = ref([]);
const navigation = ref([]);
const refactoring = ref([]);
const store = useTableStore();
onMounted(() => {
store.fetchTable();
edition.value = store.getEdition;
compilation.value = store.getCompilation;
debug.value = store.getDebug;
navigation.value = store.getNavigation;
refactoring.value = store.getRefactoring;
});
Values doesn´t fill it. Is strange, if use watcher like this
edition.value = store.getEdition.filter((edition: String) => {
for (let key in edition) {
if (
edition[key].toLowerCase().includes(searchInput.value.toLowerCase())
) {
return true;
}
}
});
Array get values.
So, the problem is: How can i get store values when view loads?
Maybe the problem is the store returns Proxy object...
UPDATE 1
I created a gist with full code
https://gist.github.com/ElHombreSinNombre/4796da5bcdcf6bf4f36f009132dd9f48
UPDATE 2
Pinia loads array data, but 'setup' can´t get it
UPDATE 3: SOLUTION
Finally i resolved the problems and upload to my Github. I used computed to get data updated. Maybe other solution was better.
https://github.com/ElHombreSinNombre/vue-shortcuts
Your onMounted lambda needs to be async, and you need to wait the fetchTable function. Edit: Try using reactive instead of ref for your arrays. Rule of thumb is ref for primitive values and reactive for objects and arrays.
const search = ref(null);
const searchInput = ref("");
const edition = reactive([]);
const compilation = reactive([]);
const debug = reactive([]);
const navigation = reactive([]);
const refactoring = reactive([]);
const store = useTableStore();
onMounted(async () => {
await store.fetchTable();
edition.push(...store.getEdition);
compilation.push(...store.getCompilation);
debug.push(...store.getDebug);
navigation.push(...store.getNavigation);
refactoring.push(...store.getRefactoring);
});
If what you need is the component to not be rendered until data is ready, you'll need a flag in your data that works along with a v-if to render the component when everything is ready, something like this:
// in your template
<div v-if="dataReady">
// your html code
</div>
// inside your script
const dataReady = ref(false)
onMounted(async () => {
await store.fetchTable();
dataReady.value = true;
});

Setting initial state using recoil in a nextjs app with ISR and SWR

I'm trying to figure out how to set my initial recoil state while still using nextjs`s ISR feature.
So I made a product.ts file inside of a states directory the file contains the following code
const productsState = atom({
key: 'productState',
default: []
})
I thought about calling my api here and instead of setting the default as an empty array have it filled with data from the api call, but I'm sure I would lose out of ISR and SWR benefits that nextjs brings?
So I thought about setting initial state inside of the getStaticProps method
export const getStaticProps: GetStaticProps = async () => {
const res: Response = await fetch("http://127.0.0.1:8000/api/products");
const {data} = await res.json();
return {
props: {
data
},
revalidate: 10
}
}
But this would only run once on build time so data would be stale, so I made a hook to get my products using SWR
import useSWR from "swr";
import { baseUrl} from '../lib/fetcher'
export const useGetProducts = (path: boolean | string, options: {} = {}) => {
if (!path) {
throw new Error("Path is required")
}
const url = baseUrl + path
const {data: products, error} = useSWR(url, options)
return {products, error}
}
this is then called inside of the page component
const Home: NextPage = ({data}: InferGetStaticPropsType<typeof getStaticProps>) => {
const {products, error} = useGetProducts('/api/products', {
initialData: data,
})
}
Now I'm just wondering if this is a viable way to set initial state in recoil without sacrificing ISR and SWR benefits?
coming from vue/nuxt I would make a global store where I would call my api and set state to the api data, but it seems in react/recoil it's a bit different?

Resources