Use Debug Value
Overview¶
use_debug_value is a ReactPy hook that writes a message to the server log whenever it changes, for the component that calls it.
use_debug_value(message, dependencies)
ReactPy components run on the server, so there is no DevTools panel to label. This is the equivalent: a value that shows up in your server's log output, tagged with the component that produced it, printed only when it differs from the last thing printed.
It needs debug mode, and logs go to the server
Nothing happens unless REACTPY_DEBUG is on. Set it before importing ReactPy — REACTPY_DEBUG=1 python app.py, or os.environ["REACTPY_DEBUG"] = "1" at the top of your entry point. Output goes through the standard logging module under the reactpy.core.hooks logger, so it lands wherever your logging configuration sends it, not in the browser console. A reader who expects DevTools will conclude the hook is broken; it is not.
Reference¶
use_debug_value(message, dependencies)¶
Call it at the top level of a component, or at the top level of one of your own hooks.
from reactpy import use_debug_value
Parameters¶
message: The value to log, or a function of no arguments that produces it. Pass a function when building the message costs something — ReactPy memoizes it, so it re-runs only when its dependencies change.-
dependencies: Only meaningful with a functionmessage. Same three shapes as every other hook that takes them:You pass Meaning dependencies=[a, b]Rebuild the message when aorbchanges.omitted (the default) Infer them from the function's closure. dependencies=NoneRebuild the message on every render.
Returns¶
None.
Caveats¶
- Silent without debug mode. With
REACTPY_DEBUGoff, the hook logs nothing — but it still computes the message. Measured: a builder function was invoked on all four renders of a component even with debug disabled. If the message is expensive, guard the expensive part yourself rather than assuming the hook skipped it. - "Changed" means
!=, not identity. Unlike every other hook, which compares dependencies withstrictly_equal, this one compares the new message against the previous one with plain!=. So a freshly constructed{"kind": "rows"}logs once and then stays quiet, because it compares equal to what was logged last time. Verified. - The comparison runs on every render, on the value you supply. For a large or slow-to-compare object that is a real cost, paid whether or not anything gets logged. This is the one hook where the docstring's performance note is worth taking literally.
- An uncomparable message breaks the component. If
message != previousraises, the exception is not caught: it surfaces as a render error in the model. A class with a__eq__that raises will fail the component, so keep messages to plain data — strings, numbers, dicts, lists. - The value is memoized like any other hook value. With a stable message, the builder function runs once; with a message that changes each render, it runs each render. Measured across four renders: stable → 1 call, changing → 4 calls.
- Like every hook, call it only at the top level — never in a loop, a conditional, or after an early
return.
Usage¶
Labelling a custom hook¶
The useful case. When a component calls three of your own hooks, a log line that says only shop(...) tells you nothing; a message naming what the hook holds tells you which hook is behaving oddly, and when.
Run this with debug on and press Add item. Each add logs one line through the reactpy.core.hooks logger, prefixed with the component and its id:
1 2 3 4 | |
Pressing a button that leaves the cart alone logs nothing, because the message did not change.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
1 | |
Passing a function as the message¶
If producing the message is more than string formatting, pass a function and let ReactPy memoize it. The report below finds the highest score to build its label; that walk happens once per distinct rows, not once per render.
Run it with debug on and press Add row:
1 2 3 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
1 | |
Keep the message honest about what it reads. A function that reads no state is computed once and then logs nothing ever again, which usually reads as "the hook stopped working" rather than as correct memoization.
Troubleshooting¶
Nothing is logged¶
Almost always debug mode. The REACTPY_DEBUG environment variable is read when reactpy.config is first imported, so setting os.environ after ReactPy is already imported has no effect — start the process with REACTPY_DEBUG=1, or set the variable before importing anything from reactpy.
The option object can also be flipped at any point, which is the version that works from inside a test or a REPL:
from reactpy.config import REACTPY_DEBUG
REACTPY_DEBUG.current = True
If debug is on and you still see nothing, the logger is being filtered. ReactPy logs at DEBUG level under reactpy.core.hooks; a configuration that shows only warnings will hide these lines.
It logs on every render¶
Your message changes every render. A timestamp, id(self), a freshly built object compared by identity, or a counter you increment during rendering all do this. Make the message describe state rather than the render.
It stopped logging¶
The message has not changed since the last line, which is the hook working as designed. If you expected a change, either the value genuinely did not change or your function is memoized on dependencies that never move — pass dependencies=None briefly to confirm which.
My component fails to render after adding it¶
You passed something whose != raises. The comparison is not guarded, so the exception becomes a render error. Pass plain data instead, and format it lazily in a function if it is large.