SolidJS
Hi! Awesome stuff. I've been pulling my hairs solving local-first. How are you guys seamlessly making this look easy. Any limitations encountered so far? What conflicts can Triplit not handle?
In any case... It would be great to have a Solid adapter for this. How much work would it take to port an adapter for SolidJS? (If it's not super difficult, I'd love to give it a shot)
Glad to hear you're excited about Triplit!
re: Conflict Limitations: Currently, Triplit treats all attributes as a Last Writer Wins Registers so it can handle most situations that are primarily CRUD. However, there are cases where an app might need more complex resolution strategies that Triplit currently doesn't support. E.g. in the future, you'll be able to directly handle Collaborative Text, Distributed Counters, Reorderable Lists, etc. Some of that is still possible currently just requires more work, like here is an example of doing collaborative text with Triplit.
re: Solid support: I think adding it would be pretty straightforward to be honest, I think you should give it a shot! Since Solid components only run once it should be pretty straightforward to wire up Triplit Subscriptions to a signal if I had to guess. Don't hesitate to ask any questions in our Discord if you want faster collaboration on it!
import { createSignal, createEffect, onCleanup, Accessor } from 'solid-js';
/**
* A primitive that subscribes to a query.
*
* @param client - The client instance accessor to query with.
* @param query - The query accessor to subscribe to.
* @param options - Optional accessor for additional options for the subscription.
* @param options.localOnly - If true, the subscription will only use the local cache. Defaults to false.
* @param options.onRemoteFulfilled - An optional callback that is called when the remote query has been fulfilled.
* @returns An object containing accessors for the fetching state, the result of the query, and any error that occurred.
*/
export function useQuery<T = any>(
client: Accessor<Client>,
query: Accessor<Query>,
options?: Accessor<QueryOptions | undefined>
) {
const [results, setResults] = createSignal<T | undefined>(undefined);
const [fetching, setFetching] = createSignal<boolean>(true);
const [fetchingLocal, setFetchingLocal] = createSignal<boolean>(true);
const [fetchingRemote, setFetchingRemote] = createSignal<boolean>(false);
const [error, setError] = createSignal<Error | undefined>(undefined);
createEffect(() => {
const currentClient = client();
const currentQuery = query();
const currentOptions = options ? options() : undefined;
// Reset state when query/client/options change before subscribing
setResults(undefined);
setFetching(true);
setFetchingLocal(true);
setFetchingRemote(true);
setError(undefined);
const unsub = currentClient.subscribeWithStatus<T>(
currentQuery,
(newVal) => {
setResults(newVal.results);
setFetching(newVal.fetching);
setFetchingLocal(newVal.fetchingLocal);
setFetchingRemote(newVal.fetchingRemote);
setError(newVal.error);
},
currentOptions
);
onCleanup(() => {
unsub();
});
});
return {
results,
fetching,
fetchingLocal,
fetchingRemote,
error,
};
}
/**
* A primitive that subscribes to the connection status of a client with the server.
*
* @param client - The client instance accessor to get the connection status of.
* @returns An object containing `status`, an accessor for the current connection status.
*/
export function useConnectionStatus(
client: Accessor<Client> // Use Accessor if client can change
) {
// Initialize with the current status
const [status, setStatus] = createSignal<ConnectionStatus>(client().connectionStatus);
createEffect(() => {
const currentClient = client(); // Track client changes
// Update signal if status changed between initial signal creation and effect run
// Or if the client instance itself changed.
const initialStatus = currentClient.connectionStatus;
if (status() !== initialStatus) {
setStatus(initialStatus);
}
const unsub = currentClient.onConnectionStatusChange(
(newStatus) => {
setStatus(newStatus);
}, true
);
onCleanup(() => {
unsub();
});
});
return {
status,
};
}
Solid bindings are now available with the @triplit/solid package -- thanks @doeixd!
Also there is a Solid.js template available with create-triplit-app e.g. yarn create triplit-app, etc