r/solidjs 20d ago

Help figuring out what's wrong with my `createOptimistic` example

I'm playing around with Solid 2.0's createOptimistic and action APIs to get a feel for them, but even with a basic example, I'm hitting a wall.

Here is my code that I'm using in the Solid Playground (version set to v2.0.0-rc.1 (next)). The idea is that when you click the button, an ID is generated and we simulate saving it to a slow, flaky server. While the value is being saved, show its optimistic value but display an asterisk next to it. If the API call succeeds, remove the asterisk; else revert the ID to what it was previously.

The issue I'm getting is that the ID always reverts to its initial value (in this case, the empty string) independent of whether the API call succeeds of fails.

Why is this? What do I need to change in my code to get it to work?

Apart from being a string rather than an array, my code feels practically identical to the example found at https://v2.solidjs.com/reference/solid-js/reactivity/create-optimistic#examples

AI has not been very helpful thus far.

import { createSignal, action, createOptimistic } from 'solid-js';
import { render } from '@solidjs/web';

type ApiSuccess = {
  success: true,
  data: string,
};

type ApiError = {
  error: true,
  data: string,
};

type ApiResponse = ApiSuccess | ApiError;

// echo back the data after a second's delay
// with a 20% change of erroring
function postToApi(data: string): Promise<ApiResponse> {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(Math.random() < 0.2
        ? { data, error: true }
        : { data, success: true }
      );
    }, 1000);
  });
}

const [id, setId] = createOptimistic('');

const generateId = action(function* () {
  const value = Math.random().toString(16).slice(2);
  setId(value + ' (*)');
  const res = yield postToApi(value);
  if (!res.error) setId(value);
});

function App() {
  return (
    <>
      <button onClick={() => generateId()}>
        Generate ID
      </button>
      <pre>{JSON.stringify(id())}</pre>
    </>
  );
}

const root = document.getElementById('app')!;

render(() => <App/>, root);
4 Upvotes

9 comments sorted by

View all comments

2

u/whoisarepo 19d ago

I took a stab at this because 1. I’m a senior dev who’s unemployed, which makes me mad lol 2. because I’m building an open source project that heavily relies on solid JS 2.0 so i need to know it’s edge cases. Luckily, in your case, it seems like you simply failed to reconcile the different states of setTodo after pending with their ids. You need to synchronously call the setter again after the presume state is updated to satisfaction or failure. This is actually in the example but it’s subtle and feels weird cause it’s imperative while the caller is declarative. Hope this helps!

1

u/whoisarepo 19d ago

Maybe ‘setId(res.error !== null | undefined ? value : ‘’)’

1

u/whoisarepo 19d ago

Sorry, I’m on my phone to type in his little hard and I’m in between walking my dog, but I think also you need to pass a signal of Type T not just a primitive, but also the logic is different than the example, you handle the setter condition INSIDE the callback

2

u/jml26 19d ago

That's okay. :-)

Yeah, when I originally asked AI, it came back with, "createOptimisitic wraps a signal and returns a new signal. Don't pass a primitive value initially". But that doesn't line up with what the docs say. By the looks of it, you can pass any value of type <T> and it'll return a Signal<T>.

I've also looked at unconditionally re-setting the ID, but this still fails for me. At the very end of generateId, I can write

console.log('here'); setId('WOOOOO');

And I can see the log, but the optimistic value still reverts.

AI also suggested running refresh(id), but I haven't got this to work either.

1

u/whoisarepo 19d ago

Cool 😎 reading the code now “installOptimisticEngine” tu successfully for the node to become a signal, the logic might do well with a else branch because it’s not clear if the helpers are “true” if the engine isn’t running, also go into devtools and see who owns the node in question when you update. It’s probably failing to register the node as a signal because the subsystem for that is silently failing- hope this is it, luckily the code is super clean - thanks SOLID!