edifice.use_state#
- edifice.use_state(initial_state)[source]#
Persistent mutable state Hook inside a
@component
function.Behaves like React useState.
- Parameters:
initial_state (
Union
[TypeVar
(_T_use_state
),Callable
[[],TypeVar
(_T_use_state
)]]) – The initial state value or initializer function.- Return type:
tuple
[TypeVar
(_T_use_state
),Callable
[[Union
[TypeVar
(_T_use_state
),Callable
[[TypeVar
(_T_use_state
)],TypeVar
(_T_use_state
)]]],None
]]- Returns:
A tuple pair containing
The current state value.
A setter function for setting or updating the state value.
use_state()
is called with an initial value. It returns a state value and a setter function.The state value will be the value of the state at the beginning of the render for this component.
The setter function will, when called, set the state value before the beginning of the next render. If the new state value is not
__eq__
to the old state value, then the component will be re-rendered.@component def Stateful(self): x, x_setter = use_state(0) Button( title=str(x) on_click = lambda _event: x_setter(x + 1) )
The setter function is referentially stable, so it will always be
__eq__
to the setter function from the previous render. It can be passed as a prop to child components.The setter function should be called inside of an event handler or a
use_effect()
function.Never call the setter function directly during a
@component
render function.Warning
The state value must not be a Callable, so that Edifice does not mistake it for an initializer function or an updater function.
If you want to store a
Callable
value, like a function, then wrap it in atuple
or some other non-Callable
data structure.Initialization#
The initial value can be either a state value or an initializer function.
An initializer function is a function of no arguments.
If an initializer function is passed to
use_state()
instead of an state value, then the initializer function will be called once before thiscomponents()
’s first render to get the initial state.def initializer() -> tuple[int]: return tuple(range(1000000)) intlist, intlist_set = use_state(initializer)
This is useful for one-time construction of initial state if the initial state is expensive to compute.
If an initializer function raises an exception then Edifice will crash.
Do not perform observable side effects inside the initializer function.
Do not write to files or network.
Do not call a setter function of another
use_state()
Hook.
For these kinds of initialization side effects, use
use_effect()
instead, oruse_async()
for very long-running initial side effects.Using the initializer function for initial side effects is good for some cases where the side effect has a predictable result and cannot fail, like for example setting global styles in the root Element, or reading small configuration files.
Update#
All updates from calling the setter function will be applied before the beginning of the next render.
A setter function can be called with a state value or an updater function.
An updater function is a function from the previous state value to the new state value.
If an updater function is passed to the setter function, then before the beginning of the next render the state value will be modified by calling all of the updater functions in the order in which they were set.
@component def Stateful(self): x, x_setter = use_state(0) def updater(x_previous:int) -> int: return x_previous + 1 Button( title=str(x) on_click = lambda _event: x_setter(updater) )
If any of the updater functions raises an exception then Edifice will crash.
State must not be mutated#
Do not mutate the state variable. The old state variable must be left unmodified so that it can be compared to the new state variable during the next render.
If Python does not have an immutable version of your state data structure, like for example the
dict
, then you just have to take care to never mutate it.Instead of mutating a state
list
, create a shallow copy of thelist
, modify the copy, then call the setter function with the modified copy.from copy import copy from typing import cast def Stateful(self): x, x_setter = use_state(cast(list[str], [])) def updater(x_previous:list[str]) -> list[str]: x_new = copy(x_previous) x_new.append("another") return x_new with View(): Button( title="Add One", on_click = lambda _event: x_setter(updater) ) for t in x: Label(text=t)
Techniques for immutable datastructures in Python#
Shallow copy. We never need a deep copy because all the data structure items are also immutable.
Frozen dataclasses. Use the replace() function to update the dataclass.
Tuples (
my_list:tuple[str, ...]
) instead of lists (my_list:list[str]
).
from typing import cast def Stateful(self): x, x_setter = use_state(cast(tuple[str, ...], ())) def updater(x_previous:tuple[str, ...]) -> tuple[str, ...]: return x_previous + ("another",) with View(): Button( title="Add One", on_click = lambda _event: x_setter(updater) ) for t in x: Label(text=t)