Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Optimistic store write is never reverted when a router action resolves without changing the data

Abierto
#620 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
48/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
typescript
Área
frontend

Línea de trabajo

Reproduce the issue with the provided Cards example, focusing on createOptimisticStore, the router query, useAction, and the router action's settlement path. Compare the listed throwing, returning, plain async, and nested Solid action variants to identify where the optimistic overlay is left in place. Done means a settled router action restores a0 b1 c2 when the underlying data is unchanged, with the regression covered by a test.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Describe the bug

When a Solid action makes an optimistic write to a store and then calls a router action that resolves without changing the underlying data, the optimistic write is never reverted. The store keeps showing the tentative value indefinitely, even after the query it derives from has refetched the unchanged data; only a page reload shows the truth.

This hits the router's documented validation pattern directly: an action that rejects the mutation by returning new Error("…") leaves the UI showing the change it just rejected. In a kanban board, moving a locked card showed the "is locked" error while the card stayed in its new column.

It happens whatever the router action resolves with (an Error, a plain value, or nothing). It does not happen when:

  • the router action throws (the overlay reverts),
  • the mutation is a plain async function followed by revalidate(getCards.key),
  • the mutation is a nested Solid action that calls revalidate(getCards.key).

This looks related to #619 (same setup: optimistic store, router query, router action), where the store's value lands but For's index accessors stay stale.

Your Example Website or App

The component below. It needs no server functions: the query and the router action are both client-side.

Steps to Reproduce the Bug or Issue

With @solidjs/[email protected], solid-js/@solidjs/web 2.0.0-rc.9 and @solidjs/[email protected] (client start mode):

import { action as routerAction, createRouter, query, useAction } from "@solidjs/router";
import { action, createMemo, createOptimisticStore, For, Loading } from "solid-js";

type Card = { id: string; order: number };
const db: Card[] = [{ id: "a", order: 0 }, { id: "b", order: 1 }, { id: "c", order: 2 }];
const wait = (ms: number) => new Promise(r => setTimeout(r, ms));

const getCards = query(async () => {
  await wait(20);
  return db.map(c => ({ ...c }));
}, "cards");

// Rejects the change without touching `db`.
const rejectSwap = routerAction(async () => {
  await wait(150);
  return new Error("rejected");
}, "reject-swap");

function Cards() {
  const data = createMemo(() => getCards());
  const [cards, setCards] = createOptimisticStore(() => data(), [] as Card[]);
  const sorted = () => [...cards].sort((x, y) => x.order - y.order);
  const call = useAction(rejectSwap);
  const swap = action(function* () {
    setCards(list => {
      const [x, y] = [...list].sort((p, q) => p.order - q.order);
      const t = x.order;
      x.order = y.order;
      y.order = t;
    });
    yield call();
  });
  return (
    <>
      <button onClick={() => swap()}>swap</button>
      <pre>{sorted().map(c => `${c.id}${c.order}`).join(" ")}</pre>
    </>
  );
}

const Router = createRouter({ routes: [{ path: "/", component: Cards }] });

export default function App() {
  return <Router>{props => <Loading fallback={<p>…</p>}>{props.children}</Loading>}</Router>;
}
  1. Load the page. It reads a0 b1 c2.
  2. Click swap. It immediately reads b0 a1 c2 (the optimistic write).
  3. Wait. It still reads b0 a1 c2 indefinitely. Reloading shows a0 b1 c2.

Replacing return new Error("rejected") with return "ok" or with no return gives the same result. Rethrowing the returned error from the generator makes no difference. With a "use server" router action the behavior is the same, in dev and in the production build, and on 2.0.0-next.28.

Expected behavior

The optimistic write reverts when the action settles, leaving a0 b1 c2, as it does when the router action throws or when the mutation is a plain async function or a nested Solid action.

Screenshots or Videos

No response

Platform
  • OS: macOS 15 (arm64)
  • Browser: Chromium 151 (Playwright headless shell)
  • Node: 24.21.0
  • Versions: @solidjs/router 2.0.0-next.30 (also 2.0.0-next.28), solid-js/@solidjs/web/@solidjs/signals 2.0.0-rc.9, @solidjs/vite-plugin 3.0.0-next.44, Vite 8.3.0
Additional context

I have not traced the root cause. The three control variants above use the same query, the same derived createOptimisticStore, and the same unchanged data, so the router action's settle path is the ingredient that leaves the overlay in place.

Lenguaje dominante
TypeScript
Estrellas
1.3k
Forks
180
Merge medio
1 d 7 h
PR fusionados (30 d)
21

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de solidjs/solid-router

Todos los issues de solidjs/solid-router

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.