[ENG-4165] Consider default and default_factory for state vars (#4510)

* [ENG-4165] Consider default and default_factory for state vars

When determining whether a state var should be marked as optional, check that
it is missing both default and default_factory and is not required.

Fix #4471

* add test for default factory with rx.foreach (#4515)

---------

Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com>
This commit is contained in:
Masen Furer 2024-12-12 10:10:44 -08:00 committed by GitHub
parent 1b6f539657
commit adfda8adfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 0 deletions

View File

@ -1097,6 +1097,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
if ( if (
not field.required not field.required
and field.default is None and field.default is None
and field.default_factory is None
and not types.is_optional(prop._var_type) and not types.is_optional(prop._var_type)
): ):
# Ensure frontend uses null coalescing when accessing. # Ensure frontend uses null coalescing when accessing.

View File

@ -1,8 +1,10 @@
from typing import Dict, List, Set, Tuple, Union from typing import Dict, List, Set, Tuple, Union
import pydantic.v1
import pytest import pytest
from reflex import el from reflex import el
from reflex.base import Base
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.core.foreach import ( from reflex.components.core.foreach import (
Foreach, Foreach,
@ -18,6 +20,12 @@ from reflex.vars.number import NumberVar
from reflex.vars.sequence import ArrayVar from reflex.vars.sequence import ArrayVar
class ForEachTag(Base):
"""A tag for testing the ForEach component."""
name: str = ""
class ForEachState(BaseState): class ForEachState(BaseState):
"""A state for testing the ForEach component.""" """A state for testing the ForEach component."""
@ -46,6 +54,8 @@ class ForEachState(BaseState):
bad_annotation_list: list = [["red", "orange"], ["yellow", "blue"]] bad_annotation_list: list = [["red", "orange"], ["yellow", "blue"]]
color_index_tuple: Tuple[int, str] = (0, "red") color_index_tuple: Tuple[int, str] = (0, "red")
default_factory_list: list[ForEachTag] = pydantic.v1.Field(default_factory=list)
class ComponentStateTest(ComponentState): class ComponentStateTest(ComponentState):
"""A test component state.""" """A test component state."""
@ -290,3 +300,11 @@ def test_foreach_component_state():
ForEachState.colors_list, ForEachState.colors_list,
ComponentStateTest.create, ComponentStateTest.create,
) )
def test_foreach_default_factory():
"""Test that the default factory is called."""
_ = Foreach.create(
ForEachState.default_factory_list,
lambda tag: text(tag.name),
)