v0.2.8
✨ This release is made possible in part by users like YOU! ✨
Breaking Changes
CopyToClipboard
component has been removed.
Use the rx.set_clipboard
special event instead.
New Features
get_event_triggers
API
The new Component.get_event_triggers
API combines the old get_triggers
and get_controlled_triggers
methods into a unified API (the previous methods will continue to work in 0.2.8, but wrapped components should move to the new API).
The new API is more flexible, providing control over handler and trigger argument mapping. The method is expected to return a dictionary of callables. The keys are the names of the triggers in the react component. The values are callables whose signature defines the javascript trigger signature and the return value is a list of args that will be passed to the python EventHandler
function.
This allows wrapping of components that accept non-standard event handler functions. Typically a lambda
is used to map arguments, but a regular function with type annotations can also be used to enable extended Var operations like indexing.
For example, the FormSelect
component uses a non-standard signature for on_change
, this can now be accomodated in the new API.
class FormSelect(rx.Component):
library = "@patternfly/react-core"
tag = "FormSelect"
def get_event_triggers(self) -> Dict[str, Any]:
return {
EventTriggers.ON_CHANGE: lambda _e, value: [value],
}
rx.download
special event
The new download event prompts the frontend browser for file download.
It can be used directly as an event trigger:
rx.button("Download file", on_click=rx.download("/uri/for/file.extension", "target_file.extension")
Or it can be yielded from a backend event handler:
def export_data(self):
...
# do export logic here and write to filepath
# then
yield rx.download(filepath, filename)
Register Serializers for Custom Types
A one-arg callable in a Reflex app or component module with @rx.serializer
decorator will be considered a serializer for the type of its argument. This allows arbitrary types used on the backend to be accessed on the frontend, as long as there is a serializer defined that returns a JSON serializable value.
For example, the following code is used to serialize plotly Figure
objects.
import json
from plotly.graph_objects import Figure
from plotly.io import to_json
@rx.serializer
def serialize_figure(figure: Figure) -> list:
return json.loads(str(to_json(figure)))["data"]
Background Task Event Handlers that do not block other Events
An async
EventHandler
function in a State
that is decorated with @rx.background
is considered a "Background Task". When triggered by the frontend or chained from another event handler, it will spawn a long running task that will NOT block other EventHandlers from running.
There are three main differences from a normal EventHandler
to be aware of:
- Background Task must be
async
- Background Task cannot directly modify state values and state values may be stale if other events have modified the state since the task started running.
- Only read or write state vars inside an
async with self
context block, where exclusive access to the latest state is guaranteed.
- Only read or write state vars inside an
- Background Task cannot be directly called from another event handler.
- Must
yield
orreturn
the background task to chain it from another handler.
- Must
class State(rx.State):
counter: int = 0
@rx.background
async def bg_task(self):
for ix in range(10):
async with self:
# state mutation is only allowed inside context block
self.counter += 1
# await long operations outside the context to avoid blocking
await asyncio.sleep(0.5)
Full documentation for this feature is still being developed. Please post comments regarding background tasks on the discussion thread.
- rx.background and StateManager.modify_state provides safe exclusive access to state by @masenf in #1676
Improvements
Support Reactivity for rx.Base
fields
Modifying fields on an rx.Base
instance will trigger updates in the state without having to reassign them. This also applies when working with fields that are container types such as list
and dict
.
Better Error Messages
- Style props with Callable Values by @ElijahAhianyo in #1751
- Type Validation for Var Operations and Enhanced Compatibility by @ElijahAhianyo in #1674
Usability
- Allow underscores in routes by @ElijahAhianyo in #1713
- Implemented color_scheme for Tabs Component by @raven-black-dream in #1792
- Simplify base rxconfig by @picklelo in #1821
- Number input float by @raven-black-dream in #1817
- Support custom styling for code in markdown by @picklelo in #1844
- add option to disable Tailwind by @ElijahAhianyo in #1842
Bug Fixes
- added check to remove local import starting from . by @wassafshahzad in #1807
- exec: print the URL, not the address the server binds to by @masenf in #1846
Minor Changes and Fixups
- use actions/checkout@v4 by @masenf in #1768
- Bump version to 0.2.7 by @picklelo in #1782
- check pyi generations are up to date by @jackie-pc in #1786
- supply default for rx.Model PK for both DB and python to work by @martinxu9 in #1788
- move dynamic imports to dedicated method by @Lendemor in #1785
- removed is_read_only from select by @wassafshahzad in #1799
- numberinput: check
_id is not None
to avoid Var truthiness warning by @masenf in #1806 - Remove deprecated route decorator by @ElijahAhianyo in #1815
- Move custom styles to root App file(_app.js) by @ElijahAhianyo in #1764
- Disable metrics in CI by @picklelo in #1822
- Remove extra imports from rx.markdown by @picklelo in #1837
- Prevent Substate class shadowing by @ElijahAhianyo in #1827
- pyproject.toml: add
packaging
to dep list by @masenf in #1839 - state: implement copy and deepcopy for MutableProxy by @masenf in #1845
- allow for non-installable imports by @Lendemor in #1843
- Format component as React string by @picklelo in #1848
- use jinja2 to render package.json by @Lendemor in #1849
- StateProxy rebinds functools.partial and methods that are bound to the proxied State by @masenf in #1853
New Contributors
- @raven-black-dream made their first contribution in #1792
Full Changelog: v0.2.7...v0.2.8