Order of `on_load` and `on_mount`

Perhaps this is a feature but the order of on_load and on_mount caught me off guard.

The output of the following:

"""Welcome to Reflex! This file outlines the steps to create a basic app."""

import reflex as rx

from rxconfig import config


class State(rx.State):
    """The app state."""
    
    @rx.event
    def do_something_on_load(self):
        print('loading page')

    @rx.event
    def do_something_on_mount(self):
        print('mounting table')


def index() -> rx.Component:
    # Welcome Page (Index)
    return rx.table.root(
            on_mount=State.do_something_on_mount,
        ),


app = rx.App()
app.add_page(index, on_load=State.do_something_on_load)

is:

App running at: http://localhost:3000
Backend running at: http://0.0.0.0:8000
mounting table
mounting table
loading page

1 - I expected the opposite order
2- I don’t know why the table is mounted twice

2 Likes

under the hood, on_mount leverages React’s useEffect hook. In dev mode we use “React strict mode”, whereby all effects are run twice, since technically they should be idempotent (running twice should not impact the state). In prod mode, the on_mount will only run once

on_mount is a “frontend driven” event, meaning when the JS code loads your page/component, the useEffect will trigger early and cause the given event handlers to run as soon as the websocket comes up.

Contrast with on_load, which is a “backend driven” event. When navigating to a new page, the frontend sends an “on_load_internal” event to the backend, which enumerates the on_load events for the requesting page and keeps the rx.State.is_hydrated flag False until all of the on_load events have been processed.

This probably sounds like greek, will try to clarify in the docs the difference between these. But let me know if you have any follow up questions.

1 Like

No, this is pretty clear, thank you.

My only comment is that in the Page Load events documentation Page Load Events (reflex.dev) it’s suggested that “the on_load=State.check_auth event would be placed on every page that requires authentication to access.”

This doesn’t seem like a good suggestion given other events could have already been triggered by the time authentication is checked.

Each event that does some protected action needs to check that the user is authenticated before taking the action. There is nothing to stop a malicious user from opening a websocket connection and sending whatever events to the backend in whatever order.

The frontend check for authentication are primarily to avoid flashing content and to redirect users to the login page if needed. The actual protected content and actions need to be protected from the backend.

Using underscore prefixed methods in the state prevents such from being triggered directly over the websocket, so typically I will define sensitive actions in such helper methods so I know they cannot be triggered without my backend calling them directly.

@masenf Are you saying that any method of a State subclass could be triggered by a malicious websocket request? I would guess that was the case before the rx.event decorator was introduced, and maybe it’s still the case now for backwards compatibility since it was introduced recently.
If so, is there a plan that only @rx.event decorated methods be possible to call from websocket requests?

Also, thanks for the description of on_mount and on_load events above. Roughly what I thought, but seeing it in writing will hopefully make it stick better in my mind.

Only methods in a state without an underscore prefix can be triggered over the websocket. The “malicious” websocket still needs to send a client_token, so it’s not like it can just access anyone’s state, but if the event handler is allowed to take some privileged action, then it needs to ensure that the token associated with that websocket is authorized to do such.

As for a plan to require @rx.event to define event handlers, i would like to see it, but there is some internal disagreement over whether that may cause too much friction for new users. what I expect will happen is the existing rx.State stays the way it is for compatiblility, but we eventually allow users to use whatever class they want as a state class, and in that case only @rx.event and @rx.var decorated functions along with rx.Field typed attributes will be handled by reflex and everything else would be left alone. This is probably a mid to late next year plan.

1 Like