ihp
ihp copied to clipboard
SSC: Support Passing In `initialValue` During Instantiation
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
any updated here?
Not yet 👍
😭, What's the main problem here, I read some ssc code just now and think It's may not a tricky.
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!
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 }
I like this idea. It's very close to how react.js works as well 👍
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?
yep that's correct. If you set configData = ()
you could use component @Counter ()
as a workaround to get the old behaviour
You may also provide a defaultConfig
, and let component
use it, and another componentWith
for the new behaviour...
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: https://github.com/leobm/ihp/commit/13a4de7aae1b682642bf9597e26f91d7cc839f3a
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....
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.
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.