denoland / fresh

The next-gen web framework.
https://fresh.deno.dev
MIT License
12.3k stars 632 forks source link

Shared State Across Islands? #1093

Closed JacobWeisenburger closed 1 year ago

JacobWeisenburger commented 1 year ago

I tried to use zustand but this happened: #1091.

Then I tried to use jotai and this happened: https://github.com/pmndrs/jotai/discussions/1854.

Is there a state manager that works with Fresh?

joaofreires commented 1 year ago

Fresh does have compatibility with Preact. I would say for you to try the Preact hooks/signals as state manager.

You can import direcly as:

import { useState } from "preact/hooks";
JacobWeisenburger commented 1 year ago

This doesn't seem to work the way you have said. Can you help?

import { useState } from 'preact/hooks'

const [ count, setCount ] = useState( 0 )

export default function Counter () {
    return <button onClick={() => {
        setCount( count => count + 1 )
    }}>Count: {count}</button>
}
error: Uncaught TypeError: Cannot read properties of undefined (reading '__H')
    at f (https://esm.sh/stable/preact@10.11.2/deno/hooks.js:2:189)
    at P (https://esm.sh/stable/preact@10.11.2/deno/hooks.js:2:323)
    at T (https://esm.sh/stable/preact@10.11.2/deno/hooks.js:2:292)
joaofreires commented 1 year ago

Could you try to move the state declaration to inside the component?

import { useState } from 'preact/hooks'

export default function Counter () {
    const [ count, setCount ] = useState(0)
    return <button onClick={() => {
        setCount(count => count + 1)
    }}>Count: {count}</button>
}

Also, you need to check if preact is on your deno.json. You will have something like:

"compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
}

And in your import_map.json:

"imports": {
    ....
    "preact": "https://esm.sh/preact@10.11.0",
    "preact/": "https://esm.sh/preact@10.11.0/",
    "preact-render-to-string": "https://esm.sh/*preact-render-to-string@5.2.4",
    "@preact/signals": "https://esm.sh/*@preact/signals@1.0.3",
    "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.0.1",
    ...
JacobWeisenburger commented 1 year ago

Could you try to move the state declaration to inside the component?

I'm wanting a store that can be used in any island.

joaofreires commented 1 year ago

You might consider using Preact Signals: https://preactjs.com/guide/v10/signals/#managing-global-app-state Or Contexts: https://preactjs.com/guide/v10/context

JacobWeisenburger commented 1 year ago

so there's no way to use zustand or jotai?

JacobWeisenburger commented 1 year ago

You might consider using Preact Signals: https://preactjs.com/guide/v10/signals/#managing-global-app-state

how do I use this with typescript?

import { signal } from '@preact/signals'

const count = signal( 0 )
// const count: any
joaofreires commented 1 year ago

so there's no way to use zustand or jotai?

If I recall correctly, @czottmann wrote an excellent article about it (which I haven't been able to find and share here). However, there's also this discussion that might provide you with new ideas: https://github.com/denoland/fresh/discussions/230 I hope it helps you.

czottmann commented 1 year ago

@joaofreires Thanks for the kind words! Was it this one? https://zottmann.org/2022/07/31/how-to-use.html

JacobWeisenburger commented 1 year ago

@joaofreires @czottmann statery is not working for me. Please help.

/** @jsx h */
import { h } from 'preact'
import {
    makeStore, useStore
} from 'https://esm.sh/statery@0.5.4?alias=react:preact/compat&deps=preact@10.8.2'

/* Make a store */
const store = makeStore( { count: 0 } )

export default function InitStore () {
    const state = useStore( store )
    return <div>{state.count}</div>
}
error: Uncaught TypeError: pragma cannot be set when runtime is automatic at file:///C:/_Software/project/islands/InitStore.tsx:1:1
  await import(entrypoint);
  ^
    at async dev (https://deno.land/x/fresh@1.1.4/src/dev/mod.ts:187:3)
    at async file:///C:/_Software/project/dev.ts:18:1
JacobWeisenburger commented 1 year ago

I'm not sure if this will work for everything, but at first glance, this seems to work How to use some of Zustand in Deno Fresh

joaofreires commented 1 year ago

Hey, I have implemented a lightweight store to share data across islands and potentially across pages (although I'm not yet sure if it's a good idea). The store is compatible with Preact hooks, so you only need to create a single store file and import the hooks from there. If you think it's worth checking out, you can find it at: https://github.com/joaofreires/FreshMint-Store

Note: this is just a sketch, so please be careful when using it.

jiawei397 commented 1 year ago

I have used this module https://deno.land/x/fresh_store@v1.0.2/mod.ts in my business, and it's okay, although I don't think it's the most elegant solution.

tunnckoCore commented 1 year ago

I'm having similar issues. I'll try the suggested solutions, but.

I'm playing with local & session storage, it seems the it's not persisted across pages and islands - meaning, when i read from there, it retirns null, but i can see them they actually sitting in the stores.

Eg, i have a page which imports an island, the island sets some kv pairs, and then when i hit another page that includes another (or the same) island - the island cannot read the state that was written. Ideas?

OldManFroggy commented 1 year ago

@tunnckoCore preact/signals should do what you need, I use preact signals myself to maintain app, and island state for a few large fresh apps currently, look at

https://fresh-with-signals.deno.dev/

and the preact/signals site

https://preactjs.com/blog/introducing-signals/

deer commented 1 year ago

@marvinhagemeister, can this be closed? It seems like what Scott provided in the most recent comment solves this problem. Additionally, I have https://github.com/denoland/fresh/pull/1311 open to document this process.

marvinhagemeister commented 1 year ago

Good call, yeah I think this can be closed.