NextJS : ISR not working for an API after revalidation - javascript

I'm trying to build an application that fetches a quote based on today's date. I'm using ISR in NextJS with the following code -
The page where I am using ISR -
export async function getStaticProps() {
const { currentTime, currentDate } = dateParsed;
const ref = bhagwadGitaRefs[currentDate];
// Here ref is - { chapter: 2, verse: 48,} hardcoded.
const { bhagwadGitaData } = await useBhagwadGitaQuote(ref);
return {
props: {
bhagwadGitaData,
},
revalidate: 10,
};
}
DateParsed.js
const dateObj = new Date();
export const dateParsed = {
currentDate: dateObj.getDate(),
currentMonth: dateObj.getMonth(),
currentYear: dateObj.getFullYear(),
currentTime: new Date(dateObj.getTime()).getHours(),
};
useBhagwadGitaQuote(ref);
export const useBhagwadGitaQuote = async (ref) => {
let { chapter, verse } = ref;
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Key': 'someapikey',
'X-RapidAPI-Host': 'rapidapi.com',
},
};
const link = `https://bhagavad-gita3.p.rapidapi.com/v2/chapters/${chapter}/verses/${verse}/`;
const raw = await fetch(link, options);
const bhagwadGitaData = await raw.json();
return { bhagwadGitaData, isLoading: !bhagwadGitaData };
};
It is designed such that the currentDate will go into the URL of the API. But when I tried running the app the next day after building it locally, it won't revalidate the data, even though the date has changed (hence the request URL will also change, so technically it should rebuild). I refreshed it multiple times but it won't rebuild that particular page again, with new date data.

Related

Dual nested dynamic routing in experimental app directory

I am using NextJS 13 and performing the following inside the app folder.
I am trying to use generateStaticParams function to achieve static generation pages on build.
This is the route: subpage/[categoryName]/[gifId]
So the route could be like following examples.
/subpage/fashion/1
/subpage/fashion/2
/subpage/fashion/3
/subpage/technology/1
/subpage/technology/2
/subpage/technology/3
/subpage/technology/4
... and so on.
The route subpage/[categoryName] won't have anything there. Might show an error or redirect some place.
The full path subpage/[categoryName]/[gifId] including the [gifId] is a must.
I need to perform REST requests to get the data for the pages.
How could I set this up inside my page.tsx file which will be located at: subpage/[categoryName]/[gifId]/page.tsx ?
If it was a single dynamic path, would be straight forward. See my implementation below for that.
But since is nested with 2 dynamic paths [categoryName] and [gifId] back to back, bit confused how to achieve this. Pls assist.
import MyComponent from "../../../components/MyComponent";
import { PartialGifProps, TagType} from "../../../utils/typings";
import axios from "axios";
import {apiDomain, defaultHeaders} from "../../../utils/constants";
const perPage = 40;
type Props = {
params: {
gifId: string,
},
}
export const generateStaticParams = async () => {
const url = `${apiDomain}/get_gif_count`; // I have access to modify the backend for this if it should contain category.
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { total_count: totalCount } : TagType = fetchGifs.data;
const totalPages = Math.ceil(totalCount / perPage);
let paramsList = [];
for (let i = 1; i <= totalPages; i++) {
paramsList.push({ gifId: i.toString() })
}
// this paramsList would look like:
// [
// { gifId: '1', },
// { gifId: '2', },
// { gifId: '3', },
// .......
// ]
return paramsList;
}
const MyPage = async ({params: {gifId}}: Props) => {
const url = `${apiDomain}/get_partial?page=${gifId}&per_page=${perPage}`;
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { gifs } : PartialGifProps = fetchGifs.data;
return (
<div className='text-white'>
<MyComponent gifs={gifs}/>
</div>
);
};
export default MyPage;
You can get categoryName in the same way you get gifId, through the params prop
type Props = {
params: {
gifId: string,
categoryName: string,
},
}
const MyPage = async ({params: {gifId, categoryName}}: Props) => {
console.log('categoryName =', categoryName);
const url = `${apiDomain}/get_partial?page=${gifId}&per_page=${perPage}`;
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { gifs } : PartialGifProps = fetchGifs.data;
return (
<div className='text-white'>
<MyComponent gifs={gifs}/>
</div>
);
};

Axios get request with parameters to filter find certrain createdAt range in mongodb

I've in my react frontend multiple dates in an Array with this format 'MM/YYYY'
Now I want to get my history from MongoDB that's createdAt the time range of one month.
How can i pass my data in this axios get request?
My Frontend
let date = '11/2022'
const getHistory = async () => {
let monthYearStart = dayjs(date, 'MM/YYYY').format('YYYY.MM.01');
let monthYearEnd = dayjs(date, 'MM/YYYY').format('YYYY.MM.32');
const res = await axios.get('/api/monthlyhistory');
setPdfHistory(res.data);
};
getHistory().then(() => {});
My Backend
try {
const history = await History.find({
status: true,
createdAt: {
$gte: dayjs(new Date(monthYearStart, 'YYYY.MM.DD')),
$lt: dayjs(new Date(monthYearEnd, 'YYYY.MM.DD')),
},
});
res.json(history);
} catch (err) {
return res.status(500).json({ msg: err.message });
}
One option would be to pass the dates as query parameters. I would recommend using ISO 8601 format to remove ambiguity use the native Date constructor
Client-side
// Note these are local dates
const monthYearStart = new Date(2022, 10); // month is a zero-based index
const monthYearEnd = new Date(monthYearStart);
monthYearEnd.setMonth(monthYearEnd.getMonth() + 1);
monthYearEnd.setDate(monthYearEnd.getDate() - 1);
const res = await axios.get("/api/monthlyhistory", {
params: {
monthYearStart: monthYearStart.toISOString(),
monthYearEnd: monthYearEnd.toISOString(),
},
});
Server-side
const { monthYearStart, monthYearEnd } = req.query;
const history = await History.find({
status: true,
createdAt: {
$gte: new Date(monthYearStart),
$lt: new Date(monthYearEnd),
},
});

VueJS Component failing to render when fetching data

I'm new to Vue.JS and JavaScript, so I have awful times debugging these applications, specially with promises and asynchronous tools. I'm trying to build my first Vue component that fetches data from somewhere. I'm using the Google Sheets API and returning some cells of a sample sheet. My component looks like this:
<template>
<ul>
<li v-for="athlete in athletes" :key="athlete">
{{ athlete }}
</li>
</ul>
</template>
<script>
import readCopaPinheiros from '#/sheets/fetchData.js';
export default {
name: 'AthletesTable',
data () {
return {
loading: false,
athletes: null
}
},
created () {
this.fetchData()
},
methods: {
fetchData() {
this.loading = true;
readCopaPinheiros('inscritos').then(values => {
this.loading = false;
console.log(values)
this.athletes = values
});
},
}
}
</script>
<style>
</style>
EDIT 1
The fetchData script:
const fs = require('fs');
const { google } = require('googleapis');
const TOKEN_PATH = '';
const CREDENTIALS_PATH = ''
const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
const {
client_secret: clientSecret,
client_id: clientId,
redirect_uris: redirectUris,
} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
clientId, clientSecret, redirectUris[0],
);
const token = fs.readFileSync(TOKEN_PATH, 'utf-8');
oAuth2Client.setCredentials(JSON.parse(token));
async function readSheet(spreadsheetId, range) {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(res => res.data.values)
.catch(err => console.log('Opa! Erro:', err));
}
function readSheetJsnofied(spreadsheetId, range) {
return readSheet(spreadsheetId, range)
.then(values => jsonifySheet(values));
}
function jsonifySheet(sheetValues) {
const header = sheetValues[0];
const values = sheetValues.slice(1);
return values.map((row) => {
const rowObj = ({});
for (let i=0; i < row.length; i++) rowObj[header[i]] = row[i];
return rowObj;
});
}
const readCopaPinheiros = d => readSheetJsnofied('sheetId', d);
export default readCopaPinheiros
For some reason the component doesn't render. I don't know what to do even to debug, all my console log tries never prints something to the console. Could someone help me understand what is going wrong?
EDIT 2
This error just shows up when trying to fetch data:
When I try to use a placeholder list with fake values directly in the data function it works. I don't believe that is a problem with the rendering itself, but how it interacts with the created and fetchData functions.
v-for="athlete in athletes"
This code only works when the athletes is an array. Initially, you set it as null so until the data from api is arrived, it will be null.
But the component still tries to render the component with your null athletes and will make the error.
You can try with this solution:
data () {
return {
loading: false,
athletes: []
}
},

How to referesh a ReactJS application automatically

I built a small boat visualizer. I am using AISHub APIs. After fetching data from the APIs I am able to obtain a json file with the vessels I am interested in and inject these vessels inside a table. The API allows to proceed for a new fetch after 1 minute as stated officially in their documentation.
The user has to manually update the page pushing the refresh button on top left of the page to see the new updated table.
The problem: Is it possible to proceed with an automatic update every minute without the user manually refreshing the page?
In order to bypass the one minute problem I had to organize a caching process and in fact that works well. I can refresh without waiting one minute, but I have to do it manually.
index.js
var express = require('express');
var router = express.Router();
var axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();
let hitCount = 0;
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/hello', async function(req, res, next) {
const allData = myCache.get('allData');
if (!allData) {
hitCount++;
console.log(`hit ${hitCount} number of times`);
const { data } = await axios.get(
'http://data.aishub.net/ws.php?username=KEY&format=1&output=json&compress=0&latmin=11.42&latmax=58.20&lonmin=-134.09&lonmax=-52.62'
);
console.log(data + 'ERR');
const [ metaData, ships ] = data;
const shipsOfInterest = ships.filter(
(ship) =>
mmsiOfInterest.includes(ship.MMSI) ||
shipNamesOfInterest.includes(ship.NAME) ||
imoOfInterest.includes(ship.IMO)
);
myCache.set('allData', shipsOfInterest, 70);
console.log(shipsOfInterest);
res.send(shipsOfInterest);
return;
}
console.log('this is the data:', allData);
res.send(allData);
});
module.exports = router;
GoogleMap.js
class BoatMap extends Component {
constructor(props) {
super(props);
this.state = {
buttonEnabled: true,
buttonClickedAt: null,
progress: 0,
ships: [],
type: 'All',
shipTypes: [],
activeShipTypes: [],
logoMap: {}
};
this.updateRequest = this.updateRequest.bind(this);
this.countDownInterval = null;
}
async componentDidMount() {
this.countDownInterval = setInterval(() => {
if (!this.state.buttonClickedAt) return;
const date = new Date();
const diff = Math.floor((date.getTime() - this.state.buttonClickedAt.getTime()) / 1000);
if (diff < 90) {
this.setState({
progress: diff,
buttonEnabled: false
});
} else {
this.setState({
progress: 0,
buttonClickedAt: null,
buttonEnabled: true
});
}
}, 500);
await this.updateRequest();
const shipTypeResults = await Client.getEntries({
content_type: 'competitors'
});
console.log(shipTypeResults);
const shipTypes = shipTypeResults.items.map((data) => data.fields);
const logoMap = shipTypes.reduce((acc, type) => {
return {
...acc,
[type.name]: type.images.fields.file.url
};
}, {});
console.log({ shipTypes });
this.setState({
logoMap
});
}
componentDidUpdate(prevProps, prevState) {
if (this.state.type !== prevState.type) {
}
}
componentWillUnmount() {
clearInterval(this.countdownInterval);
}
async updateRequest() {
const url = 'http://localhost:3001/hello';
console.log(url);
const fetchingData = await fetch(url);
const ships = await fetchingData.json();
console.log(ships);
this.setState({
buttonEnabled: false,
buttonClickedAt: new Date(),
progress: 0,
ships
});
setTimeout(() => {
this.setState({ buttonEnabled: true });
});
}
render() {
return (
<div className="google-map">
<GoogleMapReact
bootstrapURLKeys={{ key: 'KEY' }}
center={{
lat: this.props.activeShip ? this.props.activeShip.latitude : 42.4,
lng: this.props.activeShip ? this.props.activeShip.longitude : -71.1
}}
zoom={8}
>
</GoogleMapReact>
</div>
);
}
}
I have been doing a lot of research on how to automatically proceed with a refresh of the page without the user doing it manually.
Thank for pointing to the right direction for solving this problem.
Not sure if I fully understand the question. What is the mapping platform? Ideally you would not refresh the page at all. You would use state to refresh the map contents with a new ajax call every 60 seconds.
If you do just want to reload every 60 seconds then the below will work.
setTimeout(function () {
location.reload();
}, 60 * 1000);

Vue and Tensorflow: Save classifier examples to localstorage

I'm using #tensorflow-models/knn-classifier to classify my models and #tensorflow-models/mobilenet to study new models.
methods: {
async init() {
// load the load mobilenet and create a KnnClassifier
this.classifier = knnClassifier.create();
this.mobilenet = await mobilenetModule.load();
},
async addExample() {
let selected = document.getElementById("options");
this.class = selected.options[selected.selectedIndex].value;
const img = tf.browser.fromPixels(this.$children[0].webcam.webcamElement);
const logits = this.mobilenet.infer(img, "conv_preds");
this.classifier.addExample(logits, parseInt(this.class));
}
How can I save to localStorage my examples, which I added to the classifier and then load them in init() method? Because currently, I'm losing all my models after the page refresh.
Sorry maybe for the wrong terminology, I'm so new in Tensorflow js.
So, after small research I managed to save and load data with the next methods:
async toDatasetObject(dataset) {
const result = await Promise.all(
Object.entries(dataset).map(async ([classId, value]) => {
const data = await value.data();
return {
label: Number(classId),
data: Array.from(data),
shape: value.shape
};
})
);
return result;
},
fromDatasetObject(datasetObject) {
return Object.entries(datasetObject).reduce(
(result, [indexString, { data, shape }]) => {
const tensor = tf.tensor2d(data, shape);
const index = Number(indexString);
result[index] = tensor;
return result;
},
{}
);
},
And then I just load it:
this.classifier.setClassifierDataset(
this.fromDatasetObject(JSON.parse(localStorage.getItem("my-data")))
);

Categories

Resources