Skip to content

DB insert calls do not immediately update query views #403

@juls0730

Description

@juls0730

Take this example from create triplit-app:

function useTodos() {
  const todosQuery = Query('todos').Order('created_at', 'DESC');
  const { results: todos, error, fetching } = useQuery(triplit, todosQuery);
  return { todos, error, fetching };
}

export default function App() {
  const [text, setText] = useState('');
  const { todos, fetching } = useTodos();
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await triplit.insert('todos', { text });
    setText('');
  };

  return (
    <div className="main-container">
      <GettingStarted />
      <div className="app-container">
        <h1>Todos</h1>
        <ConnectionStatus />
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            placeholder="What needs to be done?"
            className="todo-input"
            value={text}
            onChange={(e) => setText(e.target.value)}
          />
          <button className="btn" type="submit" disabled={!text}>
            Add Todo
          </button>
        </form>
        {fetching && <p>Loading...</p>}
        {todos && (
          <div className="todos-container">
            {todos?.map((todo) => <Todo key={todo.id} todo={todo} />)}
          </div>
        )}
      </div>
    </div>
  );
}

In handleSubmit, if you try to read the value of todos immediately after the the insert, like so:

const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  await triplit.insert('todos', { text });
  console.log('todo added', todos, fetching);
  setText('');
};

the new todo will be missing from the value and you will get todo added [] instead of todo added [{ text: 'foo' }] like you ideally would. I believe this is related, because if I wait 20ms before console.log-ing the todos, I can see them :

this.db.onCommit(
// @ts-expect-error
throttle(
async (tx) => {
await this.db.updateQueryViews();
this.db.broadcastToQuerySubscribers();
await this.syncEngine.syncWrites();
},
20,
{ leading: false, trailing: true }
)
);
I also think
await this.ivm.bufferChanges(changes);
for (const listener of this.onCommitListeners) {
listener(changes);
}
return output;
}
may be related since the callback that actually updates query views is asynchronous, and all onCommitListeners are. I've done a bit of changing things trying to get things to work how I want it to, but I havent managed to find anything that has worked yet, and its kinda 2 am and I should sleep. So far I have tried:

  • changing the throttling debounce to leading edge (doesnt work because the main thing is actually that its asynchronous I'm pretty sure)
  • awaiting a Promise.all of the onCommitListeners listener callbacks
    I can see how debounce is desirable, but if I await a db insert call I feel like my query views should be updated. I can do the
await triplit.db.updateQueryViews();
triplit.db.broadcastToQuerySubscribers();

myself manually, but that doesnt feel ideal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions