DylanDev
DylanDev3mo ago

Supposed to return ids in random order but mostly does one image

I'm trying to pull down 5 document entries in my "levels" table and it seems to only ever return the ids in the same order most of the time. Every once and a while and sometimes when I have to refresh the npx convex dev server, will it then pull a different image. Is my code wrong?
import { v } from "convex/values";

import { mutation, query } from "./_generated/server";
import { Doc, Id } from "./_generated/dataModel";

// Gets the number of random Levels from the database
export const getRandomLevels = query({
handler: async (ctx) => {
// TODO: Implement a way to choose number of levels based on backend settings
const numOfLevels = 5;
const levels = await ctx.db.query("levels").collect();

if(levels.length < numOfLevels) {
throw new Error("Not enough levels to complete request!");
}

console.log(levels);

const selectedLevels = [];

for(let i = 0; i < numOfLevels; i++) {
const randomIndex = Math.floor(Math.random() * levels.length);
selectedLevels[i] = levels[randomIndex];
levels.splice(randomIndex, 1);
}

// Extract and return the Ids of the selected levels
const levelIds = selectedLevels.map(level => level._id);
console.log("[SERVER SIDE] Ids", levelIds);
return levelIds;
}
});

export const getImageSrc = query({
args: { id: v.id("levels") },
handler: async (ctx, args) => {
if(!args.id) {
throw new Error("Missing entryId parameter.");
}

const level = await ctx.db.get(args.id);

if(!level) {
throw new Error("No levels exist");
}

const imageUrl = await ctx.storage.getUrl(level.imageId);

return imageUrl;
}
});
import { v } from "convex/values";

import { mutation, query } from "./_generated/server";
import { Doc, Id } from "./_generated/dataModel";

// Gets the number of random Levels from the database
export const getRandomLevels = query({
handler: async (ctx) => {
// TODO: Implement a way to choose number of levels based on backend settings
const numOfLevels = 5;
const levels = await ctx.db.query("levels").collect();

if(levels.length < numOfLevels) {
throw new Error("Not enough levels to complete request!");
}

console.log(levels);

const selectedLevels = [];

for(let i = 0; i < numOfLevels; i++) {
const randomIndex = Math.floor(Math.random() * levels.length);
selectedLevels[i] = levels[randomIndex];
levels.splice(randomIndex, 1);
}

// Extract and return the Ids of the selected levels
const levelIds = selectedLevels.map(level => level._id);
console.log("[SERVER SIDE] Ids", levelIds);
return levelIds;
}
});

export const getImageSrc = query({
args: { id: v.id("levels") },
handler: async (ctx, args) => {
if(!args.id) {
throw new Error("Missing entryId parameter.");
}

const level = await ctx.db.get(args.id);

if(!level) {
throw new Error("No levels exist");
}

const imageUrl = await ctx.storage.getUrl(level.imageId);

return imageUrl;
}
});
6 Replies
Convex Bot
Convex Bot3mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
DylanDev
DylanDevOP3mo ago
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";
import React, { createContext, useState, useEffect, useContext } from "react";
import { Id } from "@/convex/_generated/dataModel";

interface GameContextType {
levels: Id<"levels">[];
currentRound: number;
score: number;
currentLevelId: Id<"levels"> | null;
currentImageSrcUrl: string;
}

const GameContext = createContext<GameContextType | null>(null);

export const GameProvider = ({
children
}: {
children: React.ReactNode;
}) => {
const [levels, setLevels] = useState<Id<"levels">[]>([]);
const [currentRound, setCurrentRound] = useState(0);
const [score, setScore] = useState(0);
const [currentLevelId, setCurrentLevel] = useState<Id<"levels"> | null>(null);
const [currentImageSrcUrl, setCurrentSrcUrl] = useState("")

const ids = useQuery(api.game.getRandomLevels);
const imageSrc = useQuery(api.game.getImageSrc, currentLevelId ? { id: currentLevelId } : "skip");

useEffect(() => {
if (ids) {
setLevels(ids);
setCurrentRound(1);
setCurrentLevel(ids[0]);
}
}, [ids]);

useEffect(() => {
if(currentLevelId) {
setCurrentSrcUrl(imageSrc ?? "");
}
}, [currentLevelId, imageSrc]);

if (ids === undefined || (currentLevelId && imageSrc === undefined)) {
// The query is still loading
/**
* TODO: Add skeleton here @Daniel
*/
return <div>Loading...</div>;
}

return (
<GameContext.Provider value={{ levels, currentRound, score, currentLevelId, currentImageSrcUrl }}>
{children}
</GameContext.Provider>
);
};

// Export the hook so that components can use game context
export const useGame = () => useContext(GameContext);
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";
import React, { createContext, useState, useEffect, useContext } from "react";
import { Id } from "@/convex/_generated/dataModel";

interface GameContextType {
levels: Id<"levels">[];
currentRound: number;
score: number;
currentLevelId: Id<"levels"> | null;
currentImageSrcUrl: string;
}

const GameContext = createContext<GameContextType | null>(null);

export const GameProvider = ({
children
}: {
children: React.ReactNode;
}) => {
const [levels, setLevels] = useState<Id<"levels">[]>([]);
const [currentRound, setCurrentRound] = useState(0);
const [score, setScore] = useState(0);
const [currentLevelId, setCurrentLevel] = useState<Id<"levels"> | null>(null);
const [currentImageSrcUrl, setCurrentSrcUrl] = useState("")

const ids = useQuery(api.game.getRandomLevels);
const imageSrc = useQuery(api.game.getImageSrc, currentLevelId ? { id: currentLevelId } : "skip");

useEffect(() => {
if (ids) {
setLevels(ids);
setCurrentRound(1);
setCurrentLevel(ids[0]);
}
}, [ids]);

useEffect(() => {
if(currentLevelId) {
setCurrentSrcUrl(imageSrc ?? "");
}
}, [currentLevelId, imageSrc]);

if (ids === undefined || (currentLevelId && imageSrc === undefined)) {
// The query is still loading
/**
* TODO: Add skeleton here @Daniel
*/
return <div>Loading...</div>;
}

return (
<GameContext.Provider value={{ levels, currentRound, score, currentLevelId, currentImageSrcUrl }}>
{children}
</GameContext.Provider>
);
};

// Export the hook so that components can use game context
export const useGame = () => useContext(GameContext);
This is where i call the functions
DylanDev
DylanDevOP3mo ago
Okay so an image finally changed. Then when I refreshed again the next one was cached
No description
DylanDev
DylanDevOP3mo ago
how do i not cache them?
lee
lee3mo ago
queries are cached when their arguments and the documents they read are the same. You can "bust" the cache by passing an argument which you can change.
const foo = query({
args: { cacheBuster: v.number() },
handler: async (ctx) => {
... function. Note you don't have to use the argument ...
},
});

// then in your frontend
const [cacheBuster, rerunQuery] = useState(0);
const result = useQuery(api.file.foo, { cacheBuster });

// and when you want to refresh the query
rerunQuery(Math.random());
const foo = query({
args: { cacheBuster: v.number() },
handler: async (ctx) => {
... function. Note you don't have to use the argument ...
},
});

// then in your frontend
const [cacheBuster, rerunQuery] = useState(0);
const result = useQuery(api.file.foo, { cacheBuster });

// and when you want to refresh the query
rerunQuery(Math.random());
lee
lee3mo ago
KeyCDN
What is Cache Busting? - KeyCDN Support
Cache busting solves browser caching issues by using a unique file version identifier to tell the browser that a new version of the file is available.