purescript-react / purescript-react-basic-hooks

An implementation of React hooks on top of purescript-react-basic
https://pursuit.purescript.org/packages/purescript-react-basic-hooks/
Apache License 2.0
200 stars 33 forks source link

React.memo recommended usage #50

Closed 2jt closed 3 years ago

2jt commented 3 years ago

Hi, what would be the recommended way to use React.memo? In this snippet barMemo { otherValue } should not have printed in its render when the text input changes. As @ptrfrncsmrph has pointed out, changing the call to barMemo otherValue would make it work. So it is because of { otherValue }, every time barMemo is called with { otherValue }, a new object is created and as React.memo uses reference equality, it is normal, that the component barMemo re-renders.

I've found myself reaching for this feature, because I have a big form and one input triggers the re-render of the rest of them. Wrapping the parts of the form in memo, I hope the performance would improve. Or am I wrong?

Thanks

megamaddu commented 3 years ago

This is the problem: React.element c <<< { nested: _ }

memo is just React's memo, so it expects an actual ReactComponent and looks through the props object with shallow reference equality.

It works if you use it directly on a ReactComponent version of your component: modified snippet

megamaddu commented 3 years ago

memo's type is kind of weird though.. I think it should just be

memo ::
  forall props.
  ReactComponent props ->
  Effect (ReactComponent props)
megamaddu commented 3 years ago

I've considered having both memo and memoReactComponent, where the latter is just React's function using ref equality and the former is forall props. Eq props => (props -> JSX) -> Component props.. but it's also already possible to write nearly the same thing using useMemo:

mkBarMemo ::
  Component
    { otherValue :: String
    }
mkBarMemo = do
  barMemo <- React.component "BarMemo" \{ otherValue } -> React.do
    React.unsafeRenderEffect $ log "this should not re-rendered"
    pure
      $ R.div_ [ R.text $ "Memo bar" ]

  React.component "BarMemoWrapper" \props -> React.do
    React.useMemo props \_ -> barMemo props
    -- ^ TryPureScript's version of react-basic-hooks is very old
    --   so to test it there you'll need to rename this to `useLazy`

Edit: to clarify, this useMemo version is using PureScript's Eq props instead of shallow reference equality. This is more "PureScript-like" but could potentially be even slower than a regular re-render if props has a complicated Eq instance.

2jt commented 3 years ago

Thank you!