npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

jotai-xstate

v0.6.0

Published

👻🤖

Downloads

14,538

Readme

jotai-xstate

👻🤖

Jotai integration library for XState

https://jotai.org/docs/integrations/xstate

Usage

atomWithActor

Creates an atom that creates, stores and manages an Actor, given it's logic. Follows mostly the same API as createActor.

const promiseLogic = fromPromise(() => fetch('http://some.host/...')) // or fromTransition, fromObservable, fromEventObservable, fromCallback, createMachine
const actorAtom = atomWithActor(promiseLogic)

const Component = () => {
    const [actorRef, send] = useAtom(actorAtom)
    ...
}

Can also be called with a second opts argument for setting up actor parameters. In typescript it's important to correctly type the actors Input, Output and Events. Refer to the examples for a full implementation

const promiseLogic = fromPromise(({ input }: { input: { id: number } }) =>
  fetch(`http://some.host/${input.id}`),
);

const actorAtom = atomWithActor(promiseLogic, { input: { id: 2 } });
//                                              ^ Will type-error if you don't provide input

Either param can also be a Getter function, allowing you to derive data from other atoms

const promiseLogicAtom = atom(
  fromPromise(({ input }: { input: { id: number } }) =>
    fetch(`http://some.host/${input.id}`),
  ),
);

const idAtom = atom(2);

const actorAtom = atomWithActor(
  (get) => get(promiseLogicAtom),
  (get) => {
    return { input: { id: get(idAtom) } };
  },
);

You can fully type all inputs, outputs and events.

type PromiseLogicOutput = string;
type PromiseLogicInput = { duration: number };
type PromiseLogicEvents =
| { type: 'elapsed'; value: number }
| { type: 'completed' };

const promiseLogicAtom = atom(
    fromPromise<PromiseLogicOutput, PromiseLogicInput, PromiseLogicEvents>(
        async ({ emit, input }) => {
            const start = Date.now();
            let now = Date.now();
            do {
                await new Promise((res) => setTimeout(res, 200));
                emit({ type: 'elapsed', value: now - start });
                now = Date.now();
            } while (now - start < input.duration);
            emit({ type: 'completed' });
            return 'Promise finished';
        },
    ),
);

const actorAtom = atomWithActor((get) => get(promiseLogicAtom), {
    input: { duration: 3000 },
});

const Component = () => {
    // actorRef allows access to the return of 'createActor'
    const [actorRef, send] = useAtom(actorAtom);

    useEffect(() => {
        const subscription = actorRef.on('elapsed', console.log);
        return () => subscription.unsubscribe;
    }, [actorRef]);

    return ...
};

Important!! By default atomWithActor will call actor.start() as soon as it mounts. To change this behaviour you can provide { autoStart: false } in your options and start it manually

const promiseLogic = fromPromise(
  () => new Promise((res) => setTimeout(res, 1000)),
); // or fromTransition, fromObservable, fromEventObservable, fromCallback, createMachine
const actorAtom = atomWithActor(promiseLogic, { autoStart: false });

const Component = () => {
  const [actorRef, send] = useAtom(actorAtom);
  return (
    <button onClick={() => actorRef.start()}>
      Click me to start the timeout
    </button>
  );
};

atomWithActorSnapshot

Provides access to an actors up-to-date snapshot while also handling it's lifecycle and listeners. Takes in an instanced actor or a getter function that returns one.

type PromiseLogicOutput = string
// or fromTransition, fromObservable, fromEventObservable, fromCallback, createMachine
const promiseLogic = fromPromise<PromiseLogicOutput>(() => new Promise(
    res => setTimeout(() => res("Return from inside logic"), 1000)
))

const actorAtom = atomWithActor(promiseLogic)
// Here get() is required because the actor logic is also stored in an atom
const actorSnapshot = atomWithActorSnapshot(get => get(actorAtom))

const Component = () => {
    const [actorRef, send] = useAtom(actorAtom)
    const [snapshot, clear] = useAtom(actorSnapshot)
    return (
        <div>
            {snapshot.state === "active" && "Waiting on timeout"}
            {snapshot.state === "done" && `Timeout done! Actor output is: ${snapshot.output}`}
        </div>
    )
        ...
}

Calling this atom's write function (named clear in the example above) will clear the internal snapshot and reset the listeners. This is usefull when combined with calling send(RESTART) on the actor logic, especially when it depends on derived values.

type PromiseLogicOutput = string
type PromiseLogicInput = { duration: number }
// or fromTransition, fromObservable, fromEventObservable, fromCallback, createMachine
const promiseLogic = fromPromise<PromiseLogicOutput, PromiseLogicInput>(({input}) => new Promise(
    res => setTimeout(() => res("Return from inside logic"), input.duration)
))

const durationAtom = atom(1000)

const actorAtom = atomWithActor(promiseLogic)
// Here get() is required because the actor logic is also stored in an atom
const actorSnapshot = atomWithActorSnapshot(get => get(actorAtom))

const Component = () => {
    const [actorRef, send] = useAtom(actorAtom)
    const [snapshot, clear] = useAtom(actorSnapshot)
    const [duration, setDuration] = useAtom(durationAtom)
    return (
        <div>
            {snapshot.state === "active" && "Waiting on timeout"}
            {snapshot.state === "done" && (
                <div>
                    Waited for {duration}ms
                    <br/>
                    Actor output is: {snapshot.output}
                    <button onClick={() => {
                      // The order here is important.
                     // First we set the duration
                      setDuration(duration + 1000)
                      // Then we reset the actor. This will start it with the new input
                      send(RESTART)
                      // Then we clear the snapshot, resetting the listeners on the new instance of the actor
                      clear()
                    }}>Wait {duration + 1000}ms</button>
                </div>
            )}
        </div>
    )
        ...
}

atomWithMachine

https://jotai.org/docs/integrations/xstate