Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Use Context Hook #585

Merged
merged 2 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/_static/css/furo-theme-overrides.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ body {
}

.sidebar-container {
width: 16em;
width: 18em;
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ it. The parent component can’t change it. This lets you add state to any compo
remove it without impacting the rest of the components.

.. card::
:link: /managing-state/shared-component-state/index
:link: /managing-state/sharing-component-state/index
:link-type: doc

:octicon:`book` Read More
Expand Down
13 changes: 0 additions & 13 deletions docs/source/escape-hatches/class-components.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/source/escape-hatches/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ Escape Hatches
.. toctree::
:hidden:

class-components
javascript-components
distributing-javascript
writing-your-own-server
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Combining Contexts and Reducers 🚧
==================================

.. note::

Under construction 🚧
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Deeply Sharing State with Contexts 🚧
=====================================

.. note::

Under construction 🚧
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. _Structuring Your State:

Structuring Your State 🚧
How to Structure State 🚧
=========================

.. note::
Expand Down
86 changes: 76 additions & 10 deletions docs/source/managing-state/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ Managing State
.. toctree::
:hidden:

structuring-your-state/index
shared-component-state/index
how-to-structure-state/index
sharing-component-state/index
when-and-how-to-reset-state/index
simplifying-updates-with-reducers/index
deeply-sharing-state-with-contexts/index
combining-contexts-and-reducers/index

.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn
:color: info
Expand All @@ -15,14 +18,15 @@ Managing State

.. grid:: 1 2 2 2

.. grid-item-card:: :octicon:`code-square` Structuring Your State
:link: structuring-your-state/index
.. grid-item-card:: :octicon:`organization` How to Structure State
:link: how-to-structure-state/index
:link-type: doc

Make it easy to reason about your application by organizing its state.
Make it easy to reason about your application with strategies for organizing
state.

.. grid-item-card:: :octicon:`link` Shared Component State
:link: shared-component-state/index
.. grid-item-card:: :octicon:`link` Sharing Component State
:link: sharing-component-state/index
:link-type: doc

Allow components to vary vary together, by lifting state into common
Expand All @@ -35,8 +39,37 @@ Managing State
Control if and how state is preserved by understanding it's relationship to
the "UI tree".

.. grid-item-card:: :octicon:`plug` Simplifying Updates with Reducers
:link: simplifying-updates-with-reducers/index
:link-type: doc

Consolidate state update logic outside your component in a single function,
called a “reducer".

.. grid-item-card:: :octicon:`broadcast` Deeply Sharing State with Contexts
:link: deeply-sharing-state-with-contexts/index
:link-type: doc

Instead of passing shared state down deep component trees, bring state into
"contexts" instead.

.. grid-item-card:: :octicon:`rocket` Combining Contexts and Reducers
:link: combining-contexts-and-reducers/index
:link-type: doc

You can combine reducers and context together to manage state of a complex
screen.

Section 4: Shared Component State

Section 1: How to Structure State
---------------------------------

.. note::

Under construction 🚧


Section 2: Shared Component State
---------------------------------

Sometimes, you want the state of two components to always change together. To do it,
Expand All @@ -49,13 +82,46 @@ state, the state represents the food name. Note how the component ``Table`` gets
at each change of state. The component is observing the state and reacting to state
changes automatically, just like it would do in React.

.. idom:: shared-component-state/_examples/synced_inputs
.. idom:: sharing-component-state/_examples/synced_inputs

.. card::
:link: shared-component-state/index
:link: sharing-component-state/index
:link-type: doc

:octicon:`book` Read More
^^^^^^^^^^^^^^^^^^^^^^^^^

Allow components to vary vary together, by lifting state into common parents.


Section 3: When and How to Reset State
--------------------------------------

.. note::

Under construction 🚧


Section 4: Simplifying Updates with Reducers
--------------------------------------------

.. note::

Under construction 🚧


Section 5: Deeply Sharing State with Contexts
---------------------------------------------

.. note::

Under construction 🚧



Section 6: Combining Contexts and Reducers
------------------------------------------

.. note::

Under construction 🚧
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
Shared Component State
======================
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 IDOM.

Sometimes you want the state of two components to always change together. To do it, you
need to be able to share state between those two components, to share state between
components move state to the nearest parent. In React world this is known as "lifting
state up" and it is a very common thing to do. Let's look at 2 examples, also from
`React <https://beta.reactjs.org/learn/sharing-state-between-components>`__,
but translated to IDOM.

Synced Inputs
-------------
Expand All @@ -18,6 +20,7 @@ and ``set_value`` variables.

.. idom:: _examples/synced_inputs


Filterable List
----------------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Simplifying Updates with Reducers 🚧
====================================

.. note::

Under construction 🚧
24 changes: 21 additions & 3 deletions docs/source/reference-material/hooks-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ There are **three important subtleties** to note about using asynchronous effect
Manual Effect Conditions
........................

In some cases, you may want to explicitely declare when an effect should be triggered.
You can do this by passing ``dependencies`` to ``use_effect``. Each of the following values
produce different effect behaviors:
In some cases, you may want to explicitly declare when an effect should be triggered.
You can do this by passing ``dependencies`` to ``use_effect``. Each of the following
values produce different effect behaviors:

- ``use_effect(..., dependencies=None)`` - triggers and cleans up on every render.
- ``use_effect(..., dependencies=[])`` - only triggers on the first and cleans up after
Expand All @@ -197,6 +197,24 @@ produce different effect behaviors:
``x`` or ``y`` have changed.


Use Context
-----------

.. code-block::

value = use_context(MyContext)

Accepts a context object (the value returned from
:func:`idom.core.hooks.create_context`) and returns the current context value for that
context. The current context value is determined by the ``value`` argument passed to the
nearest ``MyContext`` in the tree.

When the nearest <MyContext.Provider> above the component updates, this Hook will
trigger a rerender with the latest context value passed to that MyContext provider. Even
if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen
starting at the component itself using useContext.


Supplementary Hooks
===================

Expand Down
4 changes: 4 additions & 0 deletions src/idom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from .core.dispatcher import Stop
from .core.events import EventHandler, event
from .core.hooks import (
create_context,
use_callback,
use_context,
use_effect,
use_memo,
use_reducer,
Expand All @@ -28,6 +30,7 @@
"Component",
"ComponentType",
"config",
"create_context",
"event",
"EventHandler",
"hooks",
Expand All @@ -42,6 +45,7 @@
"run",
"Stop",
"use_callback",
"use_context",
"use_effect",
"use_memo",
"use_reducer",
Expand Down
25 changes: 25 additions & 0 deletions src/idom/core/_thread_local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from threading import Thread, current_thread
from typing import Callable, Generic, TypeVar
from weakref import WeakKeyDictionary


_StateType = TypeVar("_StateType")


class ThreadLocal(Generic[_StateType]):
"""Utility for managing per-thread state information"""

def __init__(self, default: Callable[[], _StateType]):
self._default = default
self._state: WeakKeyDictionary[Thread, _StateType] = WeakKeyDictionary()

def get(self) -> _StateType:
thread = current_thread()
if thread not in self._state:
state = self._state[thread] = self._default()
else:
state = self._state[thread]
return state

def set(self, state: _StateType) -> None:
self._state[current_thread()] = state
5 changes: 4 additions & 1 deletion src/idom/core/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

def component(
function: Callable[..., Union[ComponentType, VdomDict | None]]
) -> Callable[..., "Component"]:
) -> Callable[..., Component]:
"""A decorator for defining a new component.

Parameters:
Expand Down Expand Up @@ -54,6 +54,9 @@ def __init__(
def render(self) -> VdomDict | ComponentType | None:
return self.type(*self._args, **self._kwargs)

def should_render(self, new: Component) -> bool:
return True

def __repr__(self) -> str:
try:
args = self._sig.bind(*self._args, **self._kwargs).arguments
Expand Down
Loading