Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSC: Support Passing In initialValue During Instantiation #936

Open
s0kil opened this issue Jun 24, 2021 · 11 comments
Open

SSC: Support Passing In initialValue During Instantiation #936

s0kil opened this issue Jun 24, 2021 · 11 comments

Comments

@s0kil
Copy link
Collaborator

s0kil commented Jun 24, 2021

componentFromState @Counter (Counter { value = 100 }) should use the provided initial state instead of relying on the initialValue defined in the Counter component module.

https://ihpframework.slack.com/archives/C01DQE0F4F8/p1624537056154200

@MurakamiKennzo
Copy link

any updated here?

@mpscholten
Copy link
Member

Not yet 👍

@MurakamiKennzo
Copy link

MurakamiKennzo commented Dec 8, 2021

😭, What's the main problem here, I read some ssc code just now and think It's may not a tricky.

image

I think the main problem is the type of run function, what about change like this:

run :: (?state :: IORef state, ?context :: ControllerContext, ?applicationContext :: ApplicationContext, ?modelContext :: ModelContext, ?connection :: Websocket.Connection) => state -> IO ()

and change initState like this:

initialState :: Maybe State -> State

Help wanted!

@fidel-ml
Copy link

fidel-ml commented Apr 1, 2022

What about something as this:

class Component state action configData | state -> action, state -> configData where
  initialState :: configData -> state
  ...

Then you may have both static and dynamic configuration data for the components (as you are able to choose the configData...).
For example

instance Component Counter CounterController Int where
  initialState n = Counter { value = n }

@mpscholten
Copy link
Member

I like this idea. It's very close to how react.js works as well 👍

@s0kil
Copy link
Collaborator Author

s0kil commented Apr 3, 2022

With class Component state action configData | state -> action, state -> configData where it will require ConfigData to be provided on every instance of the component, correct? component @Counter configData, I can no longer do component @Counter, or am I misunderstanding something?

@mpscholten
Copy link
Member

yep that's correct. If you set configData = () you could use component @Counter () as a workaround to get the old behaviour

@fidel-ml
Copy link

fidel-ml commented Apr 6, 2022

You may also provide a defaultConfig, and let component use it, and another componentWith for the new behaviour...

@leobm
Copy link
Contributor

leobm commented Dec 11, 2022

Hi @fidel-ml, I had tested what you have suggested. But notice that the idea does not work quite as thought. @MurakamiKennzo is right, the problem is the run method where the initialState which is executed at ComponentsController initialization. At that point I don't have the props value yet. Somehow when I call IHP.ServerSideComponent.ViewFunctions componentWithProps the value I pass must be stored in the ControllerContext or somehow? But I have too little understanding of haskell that I probably can't solve this. Who can help?

Here see you what I have:
leobm@13a4de7

Edit:
You could maybe put the props as data attribute into the html and send it somehow with the first websocket call and set it before you change the state. But this is probably a stupid idea....

@mpscholten
Copy link
Member

You could maybe put the props as data attribute into the html and send it somehow with the first websocket call and set it before you change the state. But this is probably a stupid idea....

I think this might actually be a good option here.

The ControllerContext cannot be used as we're having two requests here. The first that renders the SSC componentWithProps Counter { value = 100 }. Then the JS connects via the WebSocket which is a second request.

We've had a similiar problem with IHP AutoRefresh (keeping state across multiple requests). AutoRefresh solves this by having a server-global session store. On the first request it creates a new AutoRefreshSession data type and stores it into the session store. It also puts a unique ID of the AutoRefreshSession into the first response via a meta tag. The AutoRefresh JS now passes the ID onto the WebSocket. The WebSocket handler looks up the correct session by the ID. This way we share state across multiple requests. Passing the state around here explicitly via JSON doesn't work here as the state here is the ControllerContext which cannot be serialized as JSON.

@leobm
Copy link
Contributor

leobm commented Dec 12, 2022

Hello Marc @mpscholten ,

I have played around a bit today. I now pass the props in the websocket URL on the first request and then set the state in the ComponentsController over it.

My example runs with it. But I really only have rudimentary Haskell knowledge, so the code is probably really bad. I've somehow managed to get there with a lot of trial and error. Here is the branch with my changes.
https://github.com/leobm/ihp/tree/feature-component-props

I also wrote a test counter component.
https://github.com/leobm/ihp-test-component-with-props

but somehow I didn't manage to link to my feature branch in default.nix. I have included my feature branch for testing simply over an IHP subdirectory.

Both projects compile. I have only strangely in Visual Code (Haskell plugin) with the language server problems. Get then e.g. Web.Component.Counter File the following error displayed:
"- Expected kind '* -> Constraint',
but 'Component Counter CounterController' has kind 'Constraint'.

  • In the instance declaration for
    'Component Counter CounterController CounterProps'typecheck
    "

do you know where the problem is?

Edit:
The way I solved it is of course a bit of a hack. In the URL I can't send any amount of data, maybe 2000 characters. For all cases this will not be enough of course.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants