I worked with Forest at CitizenNet writing Ocelot (the YouTube video), so I can confirm that his comment is specifically to do with React’s “everything is a component!” philosophy and developer convenience, not performance. At the time it was more common for people to try to copy React patterns directly into Halogen, like making the smallest things (like buttons) components. In Halogen this gets pretty annoying, and it’s not necessary.
But there’s also a difference in terminology; what you would call a pure function returning HTML in Halogen you might call a function component or a ‘pure, functional component’ in React; but no one would call this function a ‘component’ in Halogen.
I think Gary and I are agreeing on this – if you can’t just pass in an input and an action to raise on click or something like that, if the reusable element really does need its own internal state that’s annoying or complicated to push up to a parent, then do the component. But it shouldn’t be the first impulse. If you can do a function, do that. Even better with Hooks!
React uses reference equality in some places to try and improve performance, but it’s a bit dodgy (see the warning in the link you provided). Halogen actually also does this: it checks reference equality on the state object itself before re-rendering. For that reason you actually shouldn’t see any work done if you update the new state to match the previous state, like this:
do
state <- H.get
H.put state
For this reason I don’t think it’s necessary to guard the state update in the Halogen guide (wouldn’t this pass an unsafeRefEq
check? @garyb). This optimization can actually cause its own confusion: Text input not tracking state, but it’s necessary due to how MonadState
is implemented in PureScript – without this check, calls to H.get
would cause re-renders.
The advice to use H.memoized eq
is good; it avoids the pitfalls of React’s PureComponent
for complex data. The H.lazy
functions are also quite nice – there’s a great article about them (written for Elm but which applies equally to PureScript):
If you’re doing things in a Hooks component you can still use memoized
, and there’s also the same useMemo
hook as exists in React if you’re memoizing particular values in the body of the hook definition.
Just be careful that checking equality with eq
isn’t even more costly than just re-rendering! You can also pass a custom equality function that just checks on things like whether the UUID is the same to save on performance.