edifice.use_memo#

edifice.use_memo(fn, dependencies=())[source]#

Hook to memoize the result of calling a function.

Behaves like React useMemo.

Parameters:
  • fn (Callable[[], TypeVar(_T_use_memo)]) – A function of no arguments which returns a value.

  • dependencies (Any) – The fn will be called to recompute the value when the dependencies are not __eq__ to the old dependencies. If dependencies is None, then the value will recompute on every render.

Return type:

TypeVar(_T_use_memo)

Returns:

The memoized value from calling fn.

Memoize an expensive computation#

Use this Hook during a render to memoize a value which is pure and non-side-effecting but expensive to compute.

Example use_memo#
x_factor, x_factor_set = use_state(1)

def expensive_computation():
    return 1000000 * x_factor

bignumber = use_memo(expensive_computation, (x_factor,))

Memoize a function definition#

This Hook also behaves like React useCallback.

Use this Hook to reduce the re-render frequency of a @component which has a prop that is a function.

This Hook can store callback function with specific bound dependencies and the callback function will only change when the specified dependencies are not __eq__ to the dependencies from the last render, so the callback function can be used as a stable prop which does not change on every render.

Memoize a function definition: The problem#

We want to present the user with a hundred buttons and give the buttons an on_click prop.

This SuperComp component will re-render every time the fastprop prop changes, so then we will have to re-render all the buttons even though the buttons don’t depend on the fastprop.

@component
def SuperComp(self, fastprop:int, slowprop:int):

    value, value_set = use_state(0)

    def value_from_slowprop():
        value_set(slowprop)

    for i in range(100):
        Button(on_click=lambda _event: value_from_slowprop())

The general solution to this kind of performance problem is to create a new @component to render the buttons. This new Buttons100 @component will only re-render when its click_handler prop changes.

@component
def Buttons100(self, click_handler:Callable[[], None]):

    for i in range(100):
        Button(on_click=lambda _event: click_handler())

@component
def SuperComp(self, fastprop:int, slowprop:int):

    value, value_set = use_state(0)

    def value_from_slowprop():
        value_set(slowprop)

    Buttons100(value_from_slowprop)

But there is a problem here, which is that the click_handler prop for the Buttons100 component is a new function value_from_slowprop every time that the SuperComp component re-renders, so it will always cause the Buttons100 to re-render.

We can’t define value_from_slowprop as a constant function declared outside of the SuperComp component because it depends on bindings to slowprop and value_set.

Memoize a function definition: The solution#

So we use the use_memo() Hook to create a callback function which only changes when slowprop changes.

And now the Buttons100 will only re-render when the slowprop changes.

(The value_set setter function does not need to be in the dependencies because each setter function is constant, so it is always __eq__ from the previous render.)

@component
def Buttons100(self, click_handler:Callable[[], None]):

    for i in range(100):
        Button(on_click=lambda _event: click_handler())

@component
def SuperComp(self, fastprop:int, slowprop:int):

    value, value_set = use_state(0)

    def value_from_slowprop():
        value_set(slowprop)

    value_from_slowprop_memo = use_memo(
        lambda: value_from_slowprop,
        slowprop,
    )

    Buttons100(value_from_slowprop_memo)

The lambda function passed to use_memo() will only be called when the slowprop changes, so the memoized value_from_slowprop function will only change when the slowprop changes.