Union and optional list elements
Union and optional list elements¶
This example shows the rest of rx[list[S]]’s type surface:
list[int | str] mixes element types in one array, and list[int] | None
distinguishes the field being unset (null) from being set to an empty
list ([]) — two genuinely different states, on the wire and in the
generated types.
Backend¶
from rxdjango import ContextChannel, rx, action
class ListTypesChannel(ContextChannel):
"""Union element types and an optional list.
`mixed` shows `list[int | str]` rendering both element types in one
array. `optional_numbers` shows an optional list: `null` (not set)
is distinct from `[]` (set, but empty).
"""
mixed = rx[list[int | str]]([1, 'two', 3])
optional_numbers = rx[list[int] | None]()
@action
async def add_number(self, value: int):
self.mixed.append(value)
@action
async def add_text(self, value: str):
self.mixed.append(value)
@action
async def clear_mixed(self):
self.mixed.clear()
@action
async def set_numbers(self, values: list[int]):
self.optional_numbers = values
@action
async def append_number(self, value: int):
self.optional_numbers.append(value)
@action
async def clear_numbers(self):
self.optional_numbers = []
@action
async def unset_numbers(self):
self.optional_numbers = None
Frontend¶
import React from 'react';
import { useChannel } from '@rxdjango/react';
import { ListTypesChannel } from '../../rx/list_types/list_types.channels';
import { Demo, Fields, Field, Button, Row, Note } from '../../components/demo';
export function ListTypesDemo() {
const channel = useChannel(ListTypesChannel);
const isUnset = channel.optional_numbers === null;
return (
<Demo>
<Fields>
<Field label="Mixed list (int | str)">
<ul className="space-y-1">
{channel.mixed.map((item, index) => (
<li key={index}>
<span className="mr-2 text-xs uppercase tracking-wide text-primary-700">
{typeof item}
</span>
<span>
{String(item)}
</span>
</li>
))}
</ul>
</Field>
<Field label="Optional numbers (list[int] | None)">
{isUnset ? (
<Note>
null (not set)
</Note>
) : (
<span>
{channel.optional_numbers!.length === 0
? 'empty list'
: channel.optional_numbers!.join(', ')}
</span>
)}
</Field>
</Fields>
<Row>
<Button onClick={() => channel.add_number(Math.floor(Math.random() * 100))}>
Add number
</Button>
<Button onClick={() => channel.add_text('word')}>
Add text
</Button>
<Button
variant="secondary"
onClick={() => channel.clear_mixed()}
>
Clear mixed
</Button>
</Row>
<Row>
<Button onClick={() => channel.set_numbers([1, 2, 3])}>
Set numbers
</Button>
<Button
variant="secondary"
onClick={() => channel.append_number(Math.floor(Math.random() * 100))}
disabled={isUnset}
>
Append number
</Button>
<Button
variant="secondary"
onClick={() => channel.clear_numbers()}
>
Clear (empty list)
</Button>
<Button
variant="secondary"
onClick={() => channel.unset_numbers()}
>
Unset (null)
</Button>
</Row>
</Demo>
);
}
export default ListTypesDemo;