reflex/docs/components/conditional_rendering.md
2024-02-26 17:18:28 +01:00

270 lines
8.0 KiB
Markdown

```python exec
import reflex as rx
from pcweb.pages.docs import vars, library
```
# Conditional Rendering
We use the `cond` component to conditionally render components. The `cond` component acts in a similar way to a conditional (ternary) operator in python, acting in a similar fashion to an `if-else` statement.
```md alert
Check out the API reference for [cond docs]({library.layout.cond.path}).
```
```python eval
rx.box(height="2em")
```
Here is a simple example to show how by checking the value of the state var `show` we can render either `blue` text or `red` text.
The first argument to the `cond` component is the condition we are checking. Here the condition is the value of the state var boolean `show`.
If `show` is `True` then the 2nd argument to the `cond` component is rendered, in this case that is `rx.text("Text 1", color="blue")`.
If `show` is `False` then the 3rd argument to the `cond` component is rendered, in this case that is `rx.text("Text 2", color="red")`.
```python demo exec
class CondSimpleState(rx.State):
show: bool = True
def change(self):
self.show = not (self.show)
def cond_simple_example():
return rx.vstack(
rx.button("Toggle", on_click=CondSimpleState.change),
rx.cond(
CondSimpleState.show,
rx.text("Text 1", color="blue"),
rx.text("Text 2", color="red"),
),
)
```
## Var Operations (negation)
You can use var operations with the `cond` component. To learn more generally about var operators check out [these docs]({vars.var_operations.path}). The logical operator `~` can be used to negate a condition. In this example we show that by negating the condition `~CondNegativeState.show` within the cond, we then render the `rx.text("Text 1", color="blue")` component when the state var `show` is negative.
```python demo exec
class CondNegativeState(rx.State):
show: bool = True
def change(self):
self.show = not (self.show)
def cond_negative_example():
return rx.vstack(
rx.text(f"Value of state var show: {CondNegativeState.show}"),
rx.button("Toggle", on_click=CondNegativeState.change),
rx.cond(
CondNegativeState.show,
rx.text("Text 1", color="blue"),
rx.text("Text 2", color="red"),
),
rx.cond(
~CondNegativeState.show,
rx.text("Text 1", color="blue"),
rx.text("Text 2", color="red"),
),
)
```
## Multiple Conditions
It is also possible to make up complex conditions using the `logical or` (|) and `logical and` (&) operators.
Here we have an example using the var operators `>=`, `<=`, `&`. We define a condition that if a person has an age between 18 and 65, including those ages, they are able to work, otherwise they cannot.
We could equally use the operator `|` to represent a `logical or` in one of our conditions.
```python demo exec
import random
class CondComplexState(rx.State):
age: int = 19
def change(self):
self.age = random.randint(0, 100)
def cond_complex_example():
return rx.vstack(
rx.button("Toggle", on_click=CondComplexState.change),
rx.text(f"Age: {CondComplexState.age}"),
rx.cond(
(CondComplexState.age >= 18) & (CondComplexState.age <=65),
rx.text("You can work!", color="green"),
rx.text("You cannot work!", color="red"),
),
)
```
## Reusing Cond
We can also reuse a `cond` component several times by defining it within a function that returns a `cond`.
In this example we define the function `render_item`. This function takes in an `item`, uses the `cond` to check if the item `is_packed`. If it is packed it returns the `item_name` with a `✔` next to it, and if not then it just returns the `item_name`.
```python demo exec
class ToDoListItem(rx.Base):
item_name: str
is_packed: bool
class CondRepeatState(rx.State):
to_do_list: list[ToDoListItem] = [
ToDoListItem(item_name="Space suit", is_packed=True),
ToDoListItem(item_name="Helmet", is_packed=True),
ToDoListItem(item_name="Back Pack", is_packed=False),
]
def render_item(item: [str, bool]):
return rx.cond(
item.is_packed,
rx.chakra.list_item(item.item_name + ' ✔'),
rx.chakra.list_item(item.item_name),
)
def packing_list():
return rx.vstack(
rx.text("Sammy's Packing List"),
rx.chakra.list(rx.foreach(CondRepeatState.to_do_list, render_item)),
)
```
## Nested Conditional
We can also nest `cond` components within each other to create more complex logic. In python we can have an `if` statement that then has several `elif` statements before finishing with an `else`. This is also possible in reflex using nested `cond` components. In this example we check whether a number is positive, negative or zero.
Here is the python logic using `if` statements:
```python
number = 0
if number > 0:
print("Positive number")
elif number == 0:
print('Zero')
else:
print('Negative number')
```
This reflex code that is logically identical:
```python demo exec
import random
class NestedState(rx.State):
num: int = 0
def change(self):
self.num = random.randint(-10, 10)
def cond_nested_example():
return rx.vstack(
rx.button("Toggle", on_click=NestedState.change),
rx.cond(
NestedState.num > 0,
rx.text(f"{NestedState.num} is Positive!", color="orange"),
rx.cond(
NestedState.num == 0,
rx.text(f"{NestedState.num} is Zero!", color="blue"),
rx.text(f"{NestedState.num} is Negative!", color="red"),
)
),
)
```
Here is a more advanced example where we have three numbers and we are checking which of the three is the largest. If any two of them are equal then we return that `Some of the numbers are equal!`.
The reflex code that follows is logically identical to doing the following in python:
```python
a = 8
b = 10
c = 2
if((a>b and a>c) and (a != b and a != c)):
print(a, " is the largest!")
elif((b>a and b>c) and (b != a and b != c)):
print(b, " is the largest!")
elif((c>a and c>b) and (c != a and c != b)):
print(c, " is the largest!")
else:
print("Some of the numbers are equal!")
```
```python demo exec
import random
class CNS(rx.State):
# CNS: CondNestedState
a: int = 8
b: int = 10
c: int = 2
def change(self):
self.a = random.randint(0, 10)
self.b = random.randint(0, 10)
self.c = random.randint(0, 10)
def cond_nested_example_2():
return rx.vstack(
rx.button("Toggle", on_click=CNS.change),
rx.text(f"a: {CNS.a}, b: {CNS.b}, c: {CNS.c}"),
rx.cond(
((CNS.a > CNS.b) & (CNS.a > CNS.c)) & ((CNS.a != CNS.b) & (CNS.a != CNS.c)),
rx.text(f"{CNS.a} is the largest!", color="green"),
rx.cond(
((CNS.b > CNS.a) & (CNS.b > CNS.c)) & ((CNS.b != CNS.a) & (CNS.b != CNS.c)),
rx.text(f"{CNS.b} is the largest!", color="orange"),
rx.cond(
((CNS.c > CNS.a) & (CNS.c > CNS.b)) & ((CNS.c != CNS.a) & (CNS.c != CNS.b)),
rx.text(f"{CNS.c} is the largest!", color="blue"),
rx.text("Some of the numbers are equal!", color="red"),
),
),
),
)
```
## Cond used as a style prop
`Cond` can also be used to show and hide content in your reflex app. In this example, we have no third argument to the `cond` operator which means that nothing is rendered if the condition is false.
```python demo exec
class CondStyleState(rx.State):
show: bool = False
img_url: str = "/preview.png"
def change(self):
self.show = not (self.show)
def cond_style_example():
return rx.vstack(
rx.button("Toggle", on_click=CondStyleState.change),
rx.cond(
CondStyleState.show,
rx.image(
src=CondStyleState.img_url,
height="25em",
width="25em",
),
),
)
```