Comparison between two modern BaaS services
Good:
- Realtime
- Alongside DB, also supports auth, file uploads, server functions, and background tasks
- Experience from end to end is very typesafe
- Backend is open-source
- use friendly, casual language | |
- don't apologize for messing up, mistakes are natural! | |
- do not add comments that only explain what the code does, only add comments for complex or unclear logic that explains _why_ it's doing what it is | |
- do not change any other surrounding code unrelated to what was asked unless necessary. if you do need to change unrelated code, say so and explain why | |
- make sure your changes take into account the current state of the code, e.g. any changes or file moves/renames that I made in between queries | |
when writing JS or TS: | |
- use explicit file extensions in JS/TS import specifiers | |
- prefer for..of over forEach | |
- do not use `e` as a shorthand for `event` |
import { createRequestHandler } from "@remix-run/express" | |
import express from "express" | |
const app = express() | |
let build | |
if (process.env.NODE_ENV === "production") { | |
build = await import("./build/server/index.js") | |
app.use(express.static("build/client")) | |
} else { |
name: Checks | |
on: | |
push: | |
branches: | |
- main | |
pull_request: | |
jobs: | |
check: |
import type { Truthy } from "./types.ts" | |
class ExtendedIterable<T> implements Iterable<T> { | |
constructor(private iterable: Iterable<T>) {} | |
*[Symbol.iterator]() { | |
yield* this.iterable | |
} | |
apply<U>(fn: (iterable: ExtendedIterable<T>) => Iterable<U>) { |
import { useDebouncedCallback } from "./useDebouncedCallback" | |
export function Example() { | |
const searchDebounced = useDebouncedCallback((query: string) => { | |
// do something with query | |
}, 500) | |
return ( | |
<input | |
onChange={(event) => { |
import { Suspense } from 'react' | |
import { api } from 'convex/_generated' | |
import { useQuerySuspense } from './useQuerySuspense' | |
function App() { | |
return ( | |
<Suspense fallback="Loading..."> | |
<TodoList /> | |
</Suspense> | |
) |
async function loop() { | |
let currentTime = performance.now() | |
while (true) { | |
const nextTime = await animationFrame() | |
const delta = nextTime - currentTime | |
// progress += delta | |
currentTime = nextTime | |
} | |
} |
import { useCallback, useInsertionEffect, useRef } from "react" | |
export function useEffectEvent<Args extends unknown[], Return>( | |
callback: (...args: Args) => Return, | |
) { | |
const ref = useRef((..._args: Args): Return => { | |
throw new Error("Cannot call an event handler while rendering.") | |
}) | |
useInsertionEffect(() => { |
with React, I like being able to describe what my UI should be, top to bottom, depending purely on props and state
with Solid, conceptually, you describe what it starts out as and how it changes over time
in Solid, this manifests as needing to wrap computed values in functions, not being able to destructure props, not being able to use early returns, and so on. I know why it's this way, but it's a tradeoff nonetheless
there's some nice stuff there too though, like how JSX elements are just DOM elements, and there's no need for the "useRef useEffect" dance, and I appreciated that when I tried Solid
all that being said, I still prefer the "start over from scratch on each render" approach, as well as the separation of "UI description" and "imperative bullshit" zones that React gives me. Solid's ecosystem is big enough that I could realistically use it for a project, but ultimately, if rendering was a bottleneck for me, I'd either pick something more React-like, or not use JS/DOM at all 🤷