Reddit API Pagination in React - javascript

I am currently trying to build a Reddit client with React. For those of you that are not familiar with Reddit, it's basically a social network that consists of thousands of communities or "subreddits" created by users. Each subreddit has posts that can contain text, images, videos or links, and can be found under the URL reddit.com/r/<subreddit_name>. Reddit's public API is very simple in that you can just add .json to this URL to get a JSON response containing the posts. Specifically, the relevant data lies at response.data.children, which is an array of objects representing the posts.
Reddit doesn't have pagination in that I can just call the API and pass something like ?page=42 as a query string. Instead, I have to pass a query string containing the value of the response.data.after property of the previous response, which is basically the ID of the last post of that response, so that Reddit can now send me posts starting with the next one after that.
Currently, my custom hook to call the API looks like this:
function usePosts(community) {
const [fetching, setFetching] = useState(false);
const [error, setError] = useState(null);
const [posts, setPosts] = useState([]);
useEffect(() => {
setFetching(true);
getPosts(community)
.then(value => {
setError(null);
setPosts(value.data.children.map(value => value.data));
})
.catch(error => setError(error))
.finally(() => setFetching(false));
}, [community]);
return [fetching, error, posts];
}
The getPosts() method simply fetches the data from the API and does some error handling in case the response is not okay.
Now on to my question: How can I best implement it so that I can not only call this hook to load the first posts (which I am already doing), but also have some way of storing the after property, and then call the hook again when I want the next few posts to be appended to the posts array? Or do I need another hook? And do I have to return after as well if I want to store it, since I am mapping the response to be only the post data? How would you solve this?

Related

I can't access the elements of an array via bracket notation in the frontend but it works in the backend

I am new to coding and I am experiencing a really strange problem. There is an array coming from a database in the backend and I want to access the single elements of the array in the frontend via bracket notation. I can access the single elements of the array in the backend. But in the frontend, I receive an error message when I try to access the elements.
Here is my GitHub repo for the project: https://github.com/LittleWing85/Homepage_For_A_Friend
And you can see the website here: https://website-for-portfolio.herokuapp.com/
In /client/src/modules/Project.js, I am retreiving an object with data from the backend and storing the object in a variable called project:
export default function Project() {
const dispatch = useDispatch();
const projectId = useSelector((state) => state.portfolio.projectId);
const [project, setProject] = useState({});
useEffect(() => {
fetch("/api/project/" + projectId)
.then((response) => response.json())
.then((data) => {
setProject(data);
});
}, [projectId]);
The object has a property with the key gallery_pictures and its value is an array. With this code I was trying to display the first element of that array:
return (
<div className="content">
...
<p>{project.gallery_pictures[0]}</p>
In the console of my browser I receive the error message project.gallery_pictures is undefined:
screenshot of error message
But if I change the code <p>{project.gallery_pictures[0]}</p> to <p>{project.gallery_pictures}</p>, the content of the array is displayed:
screenshot of displayed content from array
For this reason I don't understand why the console says that project.gallery_pictures is undefined.
I tried to access the elements of this array in the backend console in /server/server.js with the same approach in this code and it worked fine:
const express = require("express");
...
app.get("/api/project/:id", (request, response) => {
getProjectDataById(request.params.id).then((result) => {
response.json(result);
console.log(Array.isArray(result.gallery_pictures));
console.log(result.gallery_pictures[0]);
> });
> });
screenshot of working code in backend
What is it that I am not getting? Why can't I access the elements of the array in the frontend?
Within the useEffect you fetch data from your sever and, as soon as you receive a response, set project to the received data. However, while waiting for the server response project is still the initial value {}, thus project.gallery_pictures is still undefined.
As a solution, you could add a condition that checks if project is still undefined, and if yes instead render a loading screen.
This is happening because the request hasn't completed by the time that your component attempts to render itself. Inside your component adding a condition will do the trick
{project?.gallery_pictures !== undefined && Array.isArray(project.gallery_pictures) ? project.gallery_pictures[0] : null}

How do I run both getDownloadURL and getMetaData at once and get the results into the same object? Firebase Storage

So I have this component where it renders out medias (images or videos) to show what are the uploaded medias (to Firebase Storage). Currently I have a useEffect hook that pulls the data from Firebase Storage and stores the data into a state. How it looks like currently:
const [uploadedMedia, setUploadedMedia] = useState([]);
useEffect(() => {
const uploadedMediaRef = ref(storage, "appTutorials/" + data.id + "/");
const getMedia = async () => {
setUploadedMedia([]);
listAll(uploadedMediaRef).then((resp) => {
resp.items.forEach((item) => {
getDownloadURL(item).then((url) => {
setUploadedMedia((prev) => [...prev, url]);
});
});
});
};
getMedia();
}, []);
What's happening is that on initial render, the app will list all the data that are present in the storage, then we loop through the data to get the download URL through getDownloadURL method that is provided by Firebase. I then put the URL into the uploadedMedia state which is then a list which later I will map through to render the images/videos.
However, what I want to do is to get the metadata through getMetaData method, so that I can know what type of media is it rendering. This is so that I can do conditional rendering on images and videos where images should be rendered using <img> tag and and videos on <iframe> or <video> tag. I want to pull both URL and metadata at once so that I can store both response in an object as such:
[
{
url: "https://link-to-media-here",
type: "png",
}
]
How can I achieve this? I imagined it should be easy as we can just do array manipulation (?) but I just can't seem to get it so far.
The API does not provide a way to combine the results of getDownloadURL and getMetaData. getDownloadURL is a bit special in that it potentially makes a change to object metadata to add a token string that provides access to the object via the returned URL. getMetaData simply fetches what's there and makes no changes. So you will have to call both in order to get everything you need. You could certainly invoke them both asynchronously and use their returned promises along with Promise.all to wait until they both complete.

my API call is not working properly [React - ASP .Net Core]

I have had a problem for a long time that I do not understand very well why it happens, I tried everything else, however I always find it in one way or another, let me explain.
I am developing a split App, Front End with React JS and ASP .net in the BackEnd, so far so good.
I designed a page in which what I am looking for is, Before the components of my page are displayed, I want to make 2 calls to my back end to bring me 2 different resources in JSON format, both resources of Type GET and I want this to be displayed information in my components. the information I ask for is nothing special, just simple JSON text strings like username, identifier, numbers etc.
To make the call to my backend I use AXIOS, and in this case I use PROMISE JavaScript in React.
To execute it when mounting my component, I make use of React's UseEffect, in this way I execute the 2 calls.
I'm not using Redux, I'm just using SessionStorage and LocalStorage, the logic in my head is as follows:
1.-if the sessionStorage called Client does not exist, I will execute the function GetCliente
2.- The GetCliente function makes calls to the API with AXIOS and brings me the information of the client
3.- When I bring it, I convert the JSON into a Javascript object with JSON.stringify and in this way I create the Client sessionStorage.
4.- When the Client sessionStorage is created, it will be mounted on a UseState called client whose initial state is null.
5.- When the client useState is filled, it will be painted on the page, to paint it I use this notation client?.username, to access its values ​​​​and if it doesn't come, it won't mark me an error.
6.- When the sessionStorage called Client is created, if the user decides to "Refresh" the page, he will no longer have to make the API call because he is already registered on the page, so he will only have to mount the information of the sessionStorage in my UseState, this way I don't overload my server with requests.
Now here comes the tricky...
My logic works, I already have it implemented and it has worked, however the following errors start appearing.
1.- Sometimes, when I enter the page for the first time, of the 2 resources that I request, 1 does not arrive, I have 2 functions, getCliente() this brings me data from a client and getWarehouse() this brings me data from a warehouse.
sometimes the information from getCustomer does not arrive but the information from getWarehouse does, and on other occasions the information from getWarehouse does not arrive but the information from getCustomer does.
I have a button called close session that cleans the sessionStorage, when I try to enter the page again it does not bring me any of the 2 resources!
I went to the Network tab of my browser, and sometimes I see that my 2 resources are executed 2 times, according to what I know is by UseEffect, that it executes my function 2 times, however in the 2 occasions the request of my 2 resources are in status 304
sometimes when I go to the network tab, I see that one of my resources is in status 304, and then that same resource is executed again by the UseEffect and the status appears in 200, a sign that it was achieved but for some reason , it doesn't load into my SessionStorage! It appears to me that the client SessionStorage was created but it is empty!
I don't know what to do anymore, I have greatly simplified this logic at this point, however sometimes it works very well and other times it doesn't!
Now yes, I attach all the code, I don't really know what I'm doing wrong, even my backend doesn't do other things like verify token or something like that, it just asks for the resource and sends it but I don't understand why it keeps happening...
First Server code this is my endpoint that brings data from my store...
[HttpGet("almacen/{dbhandle:int}/{idcaja:int}")]
public ActionResult<string> getAlmacen(int dbhandle, int idcaja) {
string [] data = ConexionDB.Instance.Obtener_almacen_caja(dbhandle, idcaja);
var dataconvert = JsonConvert.SerializeObject(data);
return Ok(dataconvert);
}
this is my endpoint that brings my user data
[HttpGet("cliente/{dbhandle:int}")]
public ActionResult<String> getcliente(int dbhandle) {
string [] data = ConexionDB.Instance.Obtener_cliente(dbhandle);
var dataconvert = JsonConvert.SerializeObject(data);
return Ok(dataconvert);
}
both endpoints work, both with PostMan I have tested it
Now code in React...
const VentasMostrador = () => {
//varibles de estado useState
const [cliente, setcliente] = useState(null);
const [almacen, setalmacen] = useState(null);
const [cajero, setcajero] = useState(null);
const [productos, setproductos] = useState([]);
const [cambio, setcambio] = useState(false);
const [total, setTotal] = useState(0);
const [show, setShow] = useState(false);
const [productoSeleccionado, setProductoSeleccionado] = useState(null);
const [seriesylotes, setseriesylotes] = useState(null);
if(!sessionStorage.getItem("ID_CAJA")){
window.location = "/PaginaPrincipal";
}
const getCliente = async () => {
let user = JSON.parse(localStorage.getItem("datauser"));
const promise = await axios.get("https://localhost:5001/api/ventasmostrador/cliente/"+user.DbHandle);
if(promise){
let data = promise.data;
let infodata = { id: data[0], nombre: data[1], codigo: data[2] };
sessionStorage.setItem("GET_CLIENTE", JSON.stringify(infodata));
setcliente(JSON.parse(sessionStorage.getItem("GET_CLIENTE")));
} else {
console.log("no se pudo cargar los datos de almacen...");
}
};
const getAlmacen = async () => {
let user = JSON.parse(localStorage.getItem("datauser"));
let caja = JSON.parse(sessionStorage.getItem("ID_CAJA"));
const promise = await axios.get("https://localhost:5001/api/ventasmostrador/almacen/"+user.DbHandle+"/"+caja);
if(promise){
let data = promise.data;
let infodata = {
id: data[0],
caja: data[1],
idalmacen: data[2],
nombrealmacen: data[3],
};
sessionStorage.setItem("GET_ALMACEN", JSON.stringify(infodata));
setalmacen(JSON.parse(sessionStorage.getItem("GET_ALMACEN")));
} else {
console.log("no se pudo cargar los datos de almacen...");
}
};
now to execute these 2 functions I make use of UseEffect.
useEffect(() => {
let datauser = JSON.parse(localStorage.getItem("datauser"));
setcajero(datauser.Nombrecajero);
}, [])
useEffect(() => {
if(!sessionStorage.getItem("GET_CLIENTE")){
getCliente();
} else {
setcliente(JSON.parse(sessionStorage.getItem("GET_CLIENTE")));
}
console.log("cuanto se ejecuta x1...");
}, []);
useEffect(() => {
if(!sessionStorage.getItem("GET_ALMACEN")){
getAlmacen();
console.log("cuanto se ejecuta x2...");
} else {
setalmacen(JSON.parse(sessionStorage.getItem("GET_ALMACEN")));
}
}, [])
Evidence of all my explanation...
This image is when I entered the application for the first time, here it executed perfectly and the data came out.
enter image description here
here the information is painted in my component, all good and in the tab where sessionStorage is located both objects are created and filled.
enter image description here
Here comes the problem, when I press the button, logout, and I leave this page, and try to enter again so that everything can be reloaded, it stops working or 1 resource, or another resource or BOTH :(
enter image description here
If I check the network tab, they appear to me that they arrived!
enter image description here
So I don't know what to do, I know the explanation is too long and tedious but this frustrates me, I don't know if the controller, useEffect, my javascript function or the promise is wrong.
If anyone reads this please help :(

Issues connecting data stream to users responses in Google Voice App

I am currently developing a voice agent to be used in a smart speaker where users will ask about some items that are being stored in a data stream. The ultimate goal is that users ask about items' names in the stream and google actions through voice will tell them the details about those items as presented in another column in the stream.
To do this, I linked a spreadsheet to Axios to stream the content of the spreadsheet as data to be read in a webhook in google actions. The link to the data stream is HERE.
Honestly, I am new to developing apps for google actions and new to javascript overall so I might be doing silly mistakes.
In the graphical interface for google actions, I am setting a type for the items I want the user to ask about.
Then, I set an intent to recognize the item as a data type and be able to send this to the webhook.
The cloud function in the webhook is as follows:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = app.types.Item;
// conv.add("This test to see if we are accessing the webhook for ${itemParam}");
data.map(item => {
if (item.Name === itemParam)
agent.add('These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}');
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
What the webhook is doing is getting the stream with the getItem function and then mapping the data to find the Name in the stream to match the item parameter (ItemParam) as identified by the user.
However, one of the main problems I have is that when trying to access the item from the user, I am using app.types.Item, but this does not work as when testing I get an error saying: "error": "Cannot read property 'Item' of undefined". I think what is happening is that I am not using the correct way to call the Item in the conversation app.
Also, I am not sure exactly how the linking to the database will work. In other works, I am not sure if
data.map(item => {
if (item.Name === itemParam)
agent.add('These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}');
will work.
I have tried multiple things to solve but I am really struggling so any help with this would be really appreciated. Also, I know that I rushed to explain things, so please let me know if you need me to explain better or clarify anything.
Thank you
There are three points I am seeing that won't work.
First, app.types.Item is not the way to get this parameter. You should instead use conv.intent.params['Item'].resolved to get the user's spoken name.
Second, you are trying to use agent.add to include text, but there is no agent in your environment. You should instead be using conv.add.
Third, the text you are sending is not properly escaped between backticks ``. It is the backtick that allows you to use template literals.
Altogether your code can be rewritten as:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = conv.intent.params['Item'].resolved;
data.map(item => {
if (item.Name === itemParam)
conv.add(`These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}`);
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

send getstaticprops data to other static pages, NextJS [duplicate]

This question already has an answer here:
Next js nested dynamic routes: data fetching via getstaticprops with shared data between routes
(1 answer)
Closed last year.
im fetching a huge json data from an external API inside my getstaticprops. this data will then be divided into parts to be send to other static pages (hundreds of pages) as props.
// page1.tsx
const page1 = ({ page1Data }) => {
...
}
const getStaticProps: GetStaticProps = async () => {
const res = await fetch('https://hugedata')
const jsonData = await res.json()
const { page1Data, page2Data, page300Data, ...allData } = jsonData
return { props: { page1Data } }
}
i only know how to send the staticprops data to the component inside the file like this.
is there a way send these data to other static pages/routes ? (e.g. to page2.tsx)
It's not really possible to send data between pages. But you can see the methods described here.
I suggest you really check router and inspect data inside it, you can find a lot of info about data on the page.
P.S. As I see, you are trying to get a lot of data and you need to use pagination and split it to the parts, you can use this lib

Categories