convex
Version:
Client for the Convex Cloud
219 lines (218 loc) • 6.81 kB
JavaScript
"use strict";
import {
InternalConvexClient
} from "../browser/index.js";
import React, { useContext, useMemo } from "react";
import { convexToJson } from "../values/index.js";
import ReactDOM from "react-dom";
import { useSubscription } from "./use_subscription.js";
export * from "./use_paginated_query.js";
export {
useQueriesGeneric
} from "./use_queries.js";
if (typeof React === "undefined") {
throw new Error("Required dependency 'react' not installed");
}
if (typeof ReactDOM === "undefined") {
throw new Error("Required dependency 'react-dom' not installed");
}
function createMutation(name, sync, update = null) {
function mutation(...args) {
assertNotAccidentalArgument(args);
return sync().mutate(name, args, update);
}
mutation.withOptimisticUpdate = function withOptimisticUpdate(optimisticUpdate) {
if (update !== null) {
throw new Error(
`Already specified optimistic update for mutation ${name}`
);
}
return createMutation(name, sync, optimisticUpdate);
};
return mutation;
}
function createAction(name, sync) {
return function(...args) {
return sync().action(name, args);
};
}
const DEFAULT_OPTIONS = {
unsavedChangesWarning: true
};
export class ConvexReactClient {
constructor(clientConfig, options) {
this.closed = false;
this.clientConfig = clientConfig;
this.listeners = /* @__PURE__ */ new Map();
this.options = { ...DEFAULT_OPTIONS, ...options };
}
get sync() {
if (this.closed) {
throw new Error("ConvexReactClient has already been closed.");
}
if (this.cachedSync) {
return this.cachedSync;
}
this.cachedSync = new InternalConvexClient(
this.clientConfig,
(updatedQueries) => this.transition(updatedQueries),
this.options
);
if (this.adminAuth) {
this.cachedSync.setAdminAuth(this.adminAuth);
}
return this.cachedSync;
}
setAuth(token) {
this.sync.setAuth(token);
}
clearAuth() {
this.sync.clearAuth();
}
setAdminAuth(token) {
this.adminAuth = token;
if (this.closed) {
throw new Error("ConvexReactClient has already been closed.");
}
if (this.cachedSync) {
this.sync.setAdminAuth(token);
}
}
watchQuery(name, args, journal) {
if (!Array.isArray(args)) {
throw new Error(
`Query arguments to \`ConvexReactClient.watchQuery\` must be an array. Received ${args}.`
);
}
return {
onUpdate: (callback) => {
const { queryToken, unsubscribe } = this.sync.subscribe(
name,
args,
journal
);
const currentListeners = this.listeners.get(queryToken);
if (currentListeners !== void 0) {
currentListeners.add(callback);
} else {
this.listeners.set(queryToken, /* @__PURE__ */ new Set([callback]));
}
return () => {
if (this.closed) {
return;
}
const currentListeners2 = this.listeners.get(queryToken);
currentListeners2.delete(callback);
if (currentListeners2.size == 0) {
this.listeners.delete(queryToken);
}
unsubscribe();
};
},
localQueryResult: () => {
if (this.cachedSync) {
return this.cachedSync.localQueryResult(name, args);
}
return void 0;
},
journal: () => {
if (this.cachedSync) {
return this.cachedSync.queryJournal(name, args);
}
return void 0;
}
};
}
mutation(name) {
return createMutation(name, () => this.sync);
}
action(name) {
return createAction(name, () => this.sync);
}
connectionState() {
return this.sync.connectionState();
}
async close() {
this.closed = true;
this.listeners = /* @__PURE__ */ new Map();
if (this.cachedSync) {
const sync = this.cachedSync;
this.cachedSync = void 0;
await sync.close();
}
}
transition(updatedQueries) {
ReactDOM.unstable_batchedUpdates(() => {
for (const queryToken of updatedQueries) {
const callbacks = this.listeners.get(queryToken);
if (callbacks) {
for (const callback of callbacks) {
callback();
}
}
}
});
}
}
const ConvexContext = React.createContext(
void 0
);
export function useConvexGeneric() {
return useContext(ConvexContext);
}
export const ConvexProvider = ({ client, children }) => {
return React.createElement(
ConvexContext.Provider,
{ value: client },
children
);
};
export function useQueryGeneric(name, ...args) {
const convex = useContext(ConvexContext);
if (convex === void 0) {
throw new Error(
"Could not find Convex client! `useQuery` must be used in the React component tree under `ConvexProvider`. Did you forget it? See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app"
);
}
const subscription = useMemo(
() => {
const watch = convex.watchQuery(name, args);
return {
getCurrentValue: () => watch.localQueryResult(),
subscribe: (callback) => watch.onUpdate(callback)
};
},
[name, convex, JSON.stringify(convexToJson(args))]
);
const queryResult = useSubscription(subscription);
return queryResult;
}
export function useMutationGeneric(name) {
const convex = useContext(ConvexContext);
if (convex === void 0) {
throw new Error(
"Could not find Convex client! `useMutation` must be used in the React component tree under `ConvexProvider`. Did you forget it? See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app"
);
}
return useMemo(() => convex.mutation(name), [convex, name]);
}
export function useActionGeneric(name) {
const convex = useContext(ConvexContext);
if (convex === void 0) {
throw new Error(
"Could not find Convex client! `useAction` must be used in the React component tree under `ConvexProvider`. Did you forget it? See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app"
);
}
return useMemo(() => convex.action(name), [convex, name]);
}
function assertNotAccidentalArgument(args) {
if (args.length !== 1)
return;
const [value] = args;
if (typeof value === "object" && "bubbles" in value && "persist" in value && "isDefaultPrevented" in value) {
throw new Error(
`Convex function called with SyntheticEvent object. Did you use a Convex function as an event handler directly? Event handlers like onClick receive an event object as their first argument. These SyntheticEvent objects are not valid Convex values. Try wrapping the function like \`const handler = () => myMutation();\` and using \`handler\` in the event handler.`
);
}
}
//# sourceMappingURL=index.js.map