Closed bentongxyz closed 2 years ago
The comment you linked is the answer to this question.
When you log to the console, it has the type Effect Unit
because there's a side effect. The side effect is a change to the world outside the program, where the number of times you run the effect and the way you order those effects matters. All programs have some kind of effect, or there'd be no reason to run them. PureScript just tells you when/where they're happening using the Effect a
type.
So why is component creation side-effecting? Because each function instance or class declaration is a separate component, even if they have the same name and were created from the same code. And it is up to you as the creator of the component to decide when that effect happens. In JS/TS it's at the module level, by convention. If you wrapped your TS component in a function and called it multiple times you'd see the same re-mounting behavior. If you wrapped your TS component in a function and immediately invoked it (const RecursiveFolder = (() => ...your existing implementation...)();
), you'd have written it exactly the way you've written it in the Try PureScript example. Using unsafePerformEffect
at the module level is the way TS/JS do it, it's just implicit because side-effecting anywhere/everywhere is the status quo in TS/JS.
The more "PureScript way" would be to keep your component definition as an Effect
and invoke that effect at the proper time in main
, before you call render
.
Because each function instance or class declaration is a separate component, even if they have the same name and were created from the same code. And it is up to you as the creator of the component to decide when that effect happens. In JS/TS it's at the module level, by convention.
thank you that makes a lot of sense.
I don't mean to beat a dead horse, but what would be the "recommended way" to approach recursive React component?
If we behave and do the "Purescript way": invoking the effect of function instance creation in main
, then the recursive component cannot directly reference itself (because that function instance has not been created yet).
So the way to do it will be to pass that instance that we created at main
as a prop back to the recursive component?
I have re-implemented the RecursiveFolder
component above using this approach Try Purescript | RecrusiveFolder as Props as an example.
The pattern I've used is to create dependent components as part of the effect for creating the parent component. I usually name components with a mk
prefix, so it's clear how this effect/thunk behavior works when reading components structured this way. The Component
alias makes this pattern easier as well.
mkApp :: Component Unit
mkApp = do
sidebar <- mkSidebar
content <- mkContent
component "App" \_ -> React.do
...
mkSidebar :: Component Unit
mkSidebar = do
component "Sidebar" \_ -> React.do
...
mkContent :: Component Unit
mkContent = do
somethingElse <- mkSomethingElse
component "Content" \_ -> React.do
...
Technically this does result in a few low level components being "duplicated", but if their parents are being re-mounted with new component types they're going to be re-mounted anyway.
Thank you! really appreciate your help :) @megamaddu
Hi there, when I try to create recursive react components, e.g. a folder structure UI:
I am hit with problems of all children components un-mounting and re-rendering (and
React.memo
also fails to work) when the parent component's state is updated.I can make it work by using impure
unsafePerformEffect
like so:I have made a minimal reproducible code at:
unsafePerformEffect
unsafePerformEffect
If I do not use
unsafePerformEffect
, the children sub components will always un-mount and re-render, and I have to useunsafeRenderEffect
in the middle of the component anyway (see below):unsafePerformEffect
unsafePerformEffect
I think the issue is (to quote your succinct comment):
Originally posted by @megamaddu in https://github.com/megamaddu/purescript-react-basic-hooks/issues/12#issuecomment-573794368
Is there no other way to achieve memoization of recursive children components in Purescript without using
unsafePerformEffect
/unsafeRenderEffect
that might have arbitrary side-effect?Is this one of the cases that Javascript/React refuses to play nicely with pure functional approach?
Thank you for reading through this long-winded issue.
Lastly, this is a Codepen | Typescript Implementation of how I would write it in "normal" Typescript, for refererence.