DEV Community

Andrei L
Andrei L

Posted on • Edited on

🐏 How React memoization works

First you should know whats happening when using React and JSX

import React from 'react'

function SomeChildren() {
    /* ... */
    return null /* ... */
}

function LinkWrapper(props) {
    /* ... */
    return null /* ... */
}

function App() {
  return (
    <LinkWrapper uselink>
      <SomeChildren />
    </LinkWrapper >
  )
}
Enter fullscreen mode Exit fullscreen mode

When compiling JSX, Function App becomes

import React from 'react'

function App() {
  return React.createElement(
    LinkWrapper, // component
    { useLink: true }, // props
    React.createElement(SomeChildren) // children
  )
}
Enter fullscreen mode Exit fullscreen mode

And at runtime when React calls your function, here is what your function will return
Replaced App return with what kind of data React.createElement is returning.

function App() {
  return {
    "$$typeof": Symbol(react.element),
    "type": LinkWrapper,
    "props": {
      "useLink": true,
      "children": {
        "$$typeof": Symbol(react.element),
        "type": SomeChildren,
        "props": {}
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

So at every call, React will always get a new definition of your App,
This will trigger to get the definition for all tree of components.
Note: This will not actually render to DOM anything. React just needs to know if anything changed.

Now for example you use React.memo to memoize your LinkWrapper

const LinkWrapper = React.memo((props) => {
  return null /* ... */
})
Enter fullscreen mode Exit fullscreen mode

This will make React to receive previous LinkWrapper return value if props were not changed.
Note: By default it will only shallowly compare complex objects in the props object.

Now let's come back at our App

function App() {
  return (
    <LinkWrapper uselink>
      <SomeChildren />
    </LinkWrapper >
  )
}
Enter fullscreen mode Exit fullscreen mode

As I explained above <SomeChildren /> always will return a new React definition.
This means that using React.memo on LinkWrapper will not have any effect.
Because children always will be a new definition.

If you want to also memoize children you will have to do it manually.

function App() {
  const memoChildren = React.useMemo(() => <SomeChildren />, [])
  return (
    <LinkWrapper uselink>
      {memoChildren}
    </LinkWrapper >
  )
}
Enter fullscreen mode Exit fullscreen mode

This can also be wrote as

function App() {
  const memoChildren = React.useMemo(() => <SomeChildren />, [])
  return (
    <LinkWrapper uselink children={memoChildren} />
  )
}
Enter fullscreen mode Exit fullscreen mode

Now memoChildren will always have same value between re-renders
Now LinkWrapper will also see that children did not change,
and will return last memoized value without calling the function again

Top comments (0)