Sharing Component State#

Note

Parts of this document are still under construction 🚧

Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as β€œlifting state up”, and it’s one of the most common things you will do writing code with ReactPy.

Synced Inputs#

In the code below the two input boxes are synchronized, this happens because they share state. The state is shared via the parent component SyncedInputs. Check the value and set_value variables.

from reactpy import component, hooks, html, run


@component
def SyncedInputs():
    value, set_value = hooks.use_state("")
    return html.p(
        Input("First input", value, set_value),
        Input("Second input", value, set_value),
    )


@component
def Input(label, value, set_value):
    def handle_change(event):
        set_value(event["target"]["value"])

    return html.label(
        label + " ", html.input({"value": value, "on_change": handle_change})
    )


run(SyncedInputs)

Filterable List#

In the example below the search input and the list of elements below share the same state, the state represents the food name.

Note how the component Table gets called at each change of state. The component is observing the state and reacting to state changes automatically, just like it would do in React.

import json
from pathlib import Path

from reactpy import component, hooks, html, run

HERE = Path(__file__)
DATA_PATH = HERE.parent / "data.json"
food_data = json.loads(DATA_PATH.read_text())


@component
def FilterableList():
    value, set_value = hooks.use_state("")
    return html.p(Search(value, set_value), html.hr(), Table(value, set_value))


@component
def Search(value, set_value):
    def handle_change(event):
        set_value(event["target"]["value"])

    return html.label(
        "Search by Food Name: ",
        html.input({"value": value, "on_change": handle_change}),
    )


@component
def Table(value, set_value):
    rows = []
    for row in food_data:
        name = html.td(row["name"])
        descr = html.td(row["description"])
        tr = html.tr(name, descr, value)
        if not value:
            rows.append(tr)
        elif value.lower() in row["name"].lower():
            rows.append(tr)
        headers = html.tr(html.td(html.b("name")), html.td(html.b("description")))
    table = html.table(html.thead(headers), html.tbody(rows))
    return table


run(FilterableList)
[
  {
    "name": "Sushi",
    "description": "Sushi is a traditional Japanese dish of prepared vinegared rice"
  },
  {
    "name": "Dal",
    "description": "The most common way of preparing dal is in the form of a soup to which onions, tomatoes and various spices may be added"
  },
  {
    "name": "Pierogi",
    "description": "Pierogi are filled dumplings made by wrapping unleavened dough around a savoury or sweet filling and cooking in boiling water"
  },
  {
    "name": "Shish Kebab",
    "description": "Shish kebab is a popular meal of skewered and grilled cubes of meat"
  },
  {
    "name": "Dim sum",
    "description": "Dim sum is a large range of small dishes that Cantonese people traditionally enjoy in restaurants for breakfast and lunch"
  }
]

Note

Try typing a food name in the search bar.