diff --git a/reacton/core.py b/reacton/core.py index 228f487..79b9eed 100644 --- a/reacton/core.py +++ b/reacton/core.py @@ -766,7 +766,9 @@ def get_widget(el: Element): raise KeyError(f"Element {el} not found in all known widgets") # for the component {context.widgets}") -def use_state(initial: T, key: str = None, eq: Callable[[Any, Any], bool] = None) -> Tuple[T, Callable[[Union[T, Callable[[T], T]]], None]]: +def use_state( + initial: Union[T, Callable[[], T]], key: str = None, eq: Callable[[Any, Any], bool] = None +) -> Tuple[T, Callable[[Union[T, Callable[[T], T]]], None]]: """Returns a `(value, setter)` tuple that is used to manage state in a component. This function can only be called from a component function. @@ -1261,6 +1263,9 @@ def use_state(self, initial, key: str = None, eq: Callable[[Any, Any], bool] = N key = str(self.context.state_index) self.context.state_index += 1 if key not in self.context.state: + if callable(initial): + initial = initial() + self.context.state[key] = initial if isinstance(initial, (list, dict, set)): self.context.state_metadata[key] = len(initial) diff --git a/reacton/core_test.py b/reacton/core_test.py index d19390b..5952e38 100644 --- a/reacton/core_test.py +++ b/reacton/core_test.py @@ -1620,6 +1620,32 @@ def update_click(click): rc.close() +def test_use_state_initial_function(): + initial_func = unittest.mock.Mock() + initial_func.return_value = 10 + + @react.component + def ButtonClick(label="Hi"): + clicks, set_clicks = react.use_state(initial_func) + + def update_click(click): + return click + 1 + + return w.Button(description=f"{label}: Clicked {clicks} times", on_click=lambda: set_clicks(update_click)) + + clicker, rc = react.render_fixed(ButtonClick()) + + initial_func.assert_called_once() + assert clicker.description == "Hi: Clicked 10 times" + + clicker.click() + + initial_func.assert_called_once() + assert clicker.description == "Hi: Clicked 11 times" + + rc.close() + + def test_use_ref(): last = None