Skip to content

Latest commit

 

History

History
123 lines (86 loc) · 3.61 KB

memo.md

File metadata and controls

123 lines (86 loc) · 3.61 KB

memo()

Definition:

A wrapper function to cache your views based on properties you pass into them.

Import & Usage:

import { memo } from "hyperapp"

// ...

memo(view, props)

Signature & Parameters:

memo : (View, IndexableData) -> VNode
Parameters Type Required?
view View yes 💯
data anything indexable (i.e. Array, Object, String) no
Return Value Type
virtual node VNode

memo() lets you take advantage of a performance optimization technique known as memoization.


Parameters

view

A view you want memoized.

data

The data to pass along to the wrapped view function instead of the state. The wrapped view is recomputed when the data for it changes.


Example

Here we have a list of numbers displayed in a regular view as well as a memoized version of the same view. One button changes the list which affects both views. Another button updates a counter which affects the counter's view and also the regular view of the list but not the memoized view of the list.

import { h, text, app, memo } from "hyperapp"

const randomHex = () => "0123456789ABCDEF"[Math.floor(Math.random() * 16)]
const randomColor = () => "#" + Array.from({ length: 6 }, randomHex).join("")

const listView = (list) =>
  h("p", {
    style: {
      backgroundColor: randomColor(),
      color: randomColor(),
    },
  }, text(list))

const MoreItems = (state) => ({ ...state, list: [...state.list, randomHex()] })
const Increment = (state) => ({ ...state, counter: state.counter + 1 })

app({
  init: {
    list: ["a", "b", "c"],
    counter: 0,
  },
  view: (state) =>
    h("main", {}, [
      h("button", { onclick: MoreItems }, text("Grow list")),
      h("button", { onclick: Increment }, text("+1 to counter")),
      h("p", {}, text(`Counter: ${state.counter}`)),
      h("p", {}, text("Regular view showing list:")),
      listView(state.list),
      h("p", {}, text("Memoized view showing list:")),
      memo(listView, state.list),
    ]),
  node: document.querySelector("main"),
})

Other Considerations

Performance

Using memo() too often will lead to degraded performance. Only use memo() when you know it will improve rendering. When in doubt, benchmark!

Memo Data Gotcha

When Hyperapp checks memo data for changes it will do index-for-index comparisons between what the data currently is with how it was in the previous state. So, any indexable type like strings and arrays can be compared with one another and in certain edge cases be considered "equal" when it comes to determining if a re-render should happen.

We can modify parts of the example from earlier to illustrate this:

// ...

const MoreItems = (state) => ({
  ...state,
  list: Array.isArray(state.list)
    ? [...state.list, randomHex()]
    : state.list + "" + randomHex(),
})

const Increment = (state) => ({
  ...state,
  counter: state.counter + 1,

  // The following should cause the memoized view to rerender but it doesn't.
  list: Array.isArray(state.list)
    ? state.list.join("")
    : state.list.split(""),
})

// ...