How to handle necessary async/await work inside of a createEffect?

I'm building a SolidJS hook library and I have some asynchronous work that needs to happen which is occurring in a createEffect. I'm currently trying to write integration tests against my hook library and running into problems because I'm currently not awaiting the Promise result which is conflicting with other things occurirng in test. The way I work around it for testing purposes is literally doing an await sleep(1000) in test to wait for that asynchronous work to finish. Is there a better way to write my createEffect in such a way where I do not have to do a sleep in test?
function createDocument<T extends DocRecord<T>>(initialDocFn: Accessor<Doc<T>>): CreateDocumentResult<T> {
const [doc, setDoc] = createSignal(initialDocFn());
const initialDocId = () => initialDocFn()._id;

const refreshDoc = async (db: Database, docId = "") => {
const storedDoc = await db.get<T>(docId).catch(initialDocFn);
setDoc(() => storedDoc);
};

createEffect(() => {
// This is the problematic instruction. How can I re-express this to properly await it?
void refreshDoc(database(), initialDocId());
});
}
function createDocument<T extends DocRecord<T>>(initialDocFn: Accessor<Doc<T>>): CreateDocumentResult<T> {
const [doc, setDoc] = createSignal(initialDocFn());
const initialDocId = () => initialDocFn()._id;

const refreshDoc = async (db: Database, docId = "") => {
const storedDoc = await db.get<T>(docId).catch(initialDocFn);
setDoc(() => storedDoc);
};

createEffect(() => {
// This is the problematic instruction. How can I re-express this to properly await it?
void refreshDoc(database(), initialDocId());
});
}
5 Replies
AlberTenez
AlberTenez4mo ago
This will depend on your implementation. You could use actions or createAsync with cache. A potential way to do it could be:
const refreshDoc = async () => {
'use server';
const getDbSomehow = ...;
const getDocIdFromParamsOrQuery = ...;
const storedDoc = await db.get<T>(docId).catch(initialDocFn);
setDoc(() => storedDoc);
};

function createDocument<T extends DocRecord<T>>(initialDocFn: Accessor<Doc<T>>): CreateDocumentResult<T> {
const doc = createAsync(refreshDoc);

const initialDocId = () => initialDocFn()._id;

// If you need to setDoc you could use useAction and useSubmission to retreive the response.
}
const refreshDoc = async () => {
'use server';
const getDbSomehow = ...;
const getDocIdFromParamsOrQuery = ...;
const storedDoc = await db.get<T>(docId).catch(initialDocFn);
setDoc(() => storedDoc);
};

function createDocument<T extends DocRecord<T>>(initialDocFn: Accessor<Doc<T>>): CreateDocumentResult<T> {
const doc = createAsync(refreshDoc);

const initialDocId = () => initialDocFn()._id;

// If you need to setDoc you could use useAction and useSubmission to retreive the response.
}
This is some ideas to your implementation, you will need to adapt to your code. Happy coding!
MaveriX89
MaveriX894mo ago
@AlberTenez What if this is all client side driven? I’m not familiar at all with server actions or createAsync. It would be my first time diving into those ideas
AlberTenez
AlberTenez4mo ago
The question coming into my mind is, if everything is client-driven, how do you securely connect to the db from the front end? Check this out: https://start.solidjs.com/core-concepts/actions and https://start.solidjs.com/core-concepts/data-loading I think will be helpful. In case you are using solid start. In case you are not using solid start, you could use createResource with and async fn https://www.solidjs.com/docs/latest/api#createresource or, you could do sth like this in a simpler way:
const refreshDoc = async (db: Database, docId = "") => {
return db.get<T>(docId);
};

function createDocument<T extends DocRecord<T>>(initialDocFn: Accessor<Doc<T>>): CreateDocumentResult<T> {
const [doc, setDoc] = createSignal(initialDocFn());
const [err, setError] = createSignal<Error | undefined>();
const initialDocId = () => initialDocFn()._id;

createEffect(() => {
// This is the problematic instruction. How can I re-express this to properly await it?
refreshDoc(database(), initialDocId()).then((doc) => {setDoc(doc)}).catch((err) => { setError(err) });
});
}
const refreshDoc = async (db: Database, docId = "") => {
return db.get<T>(docId);
};

function createDocument<T extends DocRecord<T>>(initialDocFn: Accessor<Doc<T>>): CreateDocumentResult<T> {
const [doc, setDoc] = createSignal(initialDocFn());
const [err, setError] = createSignal<Error | undefined>();
const initialDocId = () => initialDocFn()._id;

createEffect(() => {
// This is the problematic instruction. How can I re-express this to properly await it?
refreshDoc(database(), initialDocId()).then((doc) => {setDoc(doc)}).catch((err) => { setError(err) });
});
}
These are the options I could think of.
SolidJS
Solid is a purely reactive library. It was designed from the ground up with a reactive core. It's influenced by reactive principles developed by previous libraries.
MaveriX89
MaveriX894mo ago
Ah, I see, I’ll expand the code snippet. I should have been clearer. I already have a reference to the database locally. The createDocument hook is returned as part of an outer closure function that provides the database signal. So just assume the DB instance already exists Think IndexedDB from the browser And yeah, not using SolidStart with the library (but I would like for it to be able to be used in that context if someone is interested)
foolswisdom
foolswisdom4mo ago
Regarding testing, the testing libraries usually have a way to skip forward in time. The resource api is useful is you want to turn a promise into a signal
Want results from more Discord servers?
Add your server
More Posts