edifice.use_callback#

edifice.use_callback(fn, dependencies)[source]#

Hook for a callback function to pass as props.

This Hook 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 will return a 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.

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.

The solution#

So we use the use_callback() 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 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 make_value_from_slowprop():
        def value_from_slowprop():
            value_set(slowprop)
        return value_from_slowprop

    value_from_slowprop = use_callback(
        make_value_from_slowprop,
        (slowprop,),
    )

    Buttons100(value_from_slowprop)
type fn:

Callable[[], Callable[[ParamSpec(_P_callback)], None]]

param fn:

A function of no arguments which creates and returns a callback function.

type dependencies:

Any

param dependencies:

The callback function will be created when the dependencies are not __eq__ to the old dependencies.

rtype:

Callable[[ParamSpec(_P_callback)], None]

returns:

A callback function.