Skip to content

Commit 82a7edf

Browse files
authored
Add Use Context Hook (#585)
* implement use_context hook * refine docs and make space for docs on contexts
1 parent 545f2f1 commit 82a7edf

File tree

23 files changed

+729
-129
lines changed

23 files changed

+729
-129
lines changed

docs/source/_static/css/furo-theme-overrides.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ body {
44
}
55

66
.sidebar-container {
7-
width: 16em;
7+
width: 18em;
88
}

docs/source/adding-interactivity/components-with-state/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ it. The parent component can’t change it. This lets you add state to any compo
340340
remove it without impacting the rest of the components.
341341

342342
.. card::
343-
:link: /managing-state/shared-component-state/index
343+
:link: /managing-state/sharing-component-state/index
344344
:link-type: doc
345345

346346
:octicon:`book` Read More

docs/source/escape-hatches/class-components.rst

-13
This file was deleted.

docs/source/escape-hatches/index.rst

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Escape Hatches
44
.. toctree::
55
:hidden:
66

7-
class-components
87
javascript-components
98
distributing-javascript
109
writing-your-own-server
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Combining Contexts and Reducers 🚧
2+
==================================
3+
4+
.. note::
5+
6+
Under construction 🚧
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Deeply Sharing State with Contexts 🚧
2+
=====================================
3+
4+
.. note::
5+
6+
Under construction 🚧

docs/source/managing-state/structuring-your-state/index.rst renamed to docs/source/managing-state/how-to-structure-state/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.. _Structuring Your State:
22

3-
Structuring Your State 🚧
3+
How to Structure State 🚧
44
=========================
55

66
.. note::

docs/source/managing-state/index.rst

+76-10
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ Managing State
44
.. toctree::
55
:hidden:
66

7-
structuring-your-state/index
8-
shared-component-state/index
7+
how-to-structure-state/index
8+
sharing-component-state/index
99
when-and-how-to-reset-state/index
10+
simplifying-updates-with-reducers/index
11+
deeply-sharing-state-with-contexts/index
12+
combining-contexts-and-reducers/index
1013

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

1619
.. grid:: 1 2 2 2
1720

18-
.. grid-item-card:: :octicon:`code-square` Structuring Your State
19-
:link: structuring-your-state/index
21+
.. grid-item-card:: :octicon:`organization` How to Structure State
22+
:link: how-to-structure-state/index
2023
:link-type: doc
2124

22-
Make it easy to reason about your application by organizing its state.
25+
Make it easy to reason about your application with strategies for organizing
26+
state.
2327

24-
.. grid-item-card:: :octicon:`link` Shared Component State
25-
:link: shared-component-state/index
28+
.. grid-item-card:: :octicon:`link` Sharing Component State
29+
:link: sharing-component-state/index
2630
:link-type: doc
2731

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

42+
.. grid-item-card:: :octicon:`plug` Simplifying Updates with Reducers
43+
:link: simplifying-updates-with-reducers/index
44+
:link-type: doc
45+
46+
Consolidate state update logic outside your component in a single function,
47+
called a “reducer".
48+
49+
.. grid-item-card:: :octicon:`broadcast` Deeply Sharing State with Contexts
50+
:link: deeply-sharing-state-with-contexts/index
51+
:link-type: doc
52+
53+
Instead of passing shared state down deep component trees, bring state into
54+
"contexts" instead.
55+
56+
.. grid-item-card:: :octicon:`rocket` Combining Contexts and Reducers
57+
:link: combining-contexts-and-reducers/index
58+
:link-type: doc
59+
60+
You can combine reducers and context together to manage state of a complex
61+
screen.
3862

39-
Section 4: Shared Component State
63+
64+
Section 1: How to Structure State
65+
---------------------------------
66+
67+
.. note::
68+
69+
Under construction 🚧
70+
71+
72+
Section 2: Shared Component State
4073
---------------------------------
4174

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

52-
.. idom:: shared-component-state/_examples/synced_inputs
85+
.. idom:: sharing-component-state/_examples/synced_inputs
5386

5487
.. card::
55-
:link: shared-component-state/index
88+
:link: sharing-component-state/index
5689
:link-type: doc
5790

5891
:octicon:`book` Read More
5992
^^^^^^^^^^^^^^^^^^^^^^^^^
6093

6194
Allow components to vary vary together, by lifting state into common parents.
95+
96+
97+
Section 3: When and How to Reset State
98+
--------------------------------------
99+
100+
.. note::
101+
102+
Under construction 🚧
103+
104+
105+
Section 4: Simplifying Updates with Reducers
106+
--------------------------------------------
107+
108+
.. note::
109+
110+
Under construction 🚧
111+
112+
113+
Section 5: Deeply Sharing State with Contexts
114+
---------------------------------------------
115+
116+
.. note::
117+
118+
Under construction 🚧
119+
120+
121+
122+
Section 6: Combining Contexts and Reducers
123+
------------------------------------------
124+
125+
.. note::
126+
127+
Under construction 🚧

docs/source/managing-state/shared-component-state/index.rst renamed to docs/source/managing-state/sharing-component-state/index.rst

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
Shared Component State
2-
======================
1+
Sharing Component State
2+
=======================
33

4+
.. note::
5+
6+
Parts of this document are still under construction 🚧
7+
8+
Sometimes, you want the state of two components to always change together. To do it,
9+
remove state from both of them, move it to their closest common parent, and then pass it
10+
down to them via props. This is known as “lifting state up”, and it’s one of the most
11+
common things you will do writing code with IDOM.
412

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

1214
Synced Inputs
1315
-------------
@@ -18,6 +20,7 @@ and ``set_value`` variables.
1820

1921
.. idom:: _examples/synced_inputs
2022

23+
2124
Filterable List
2225
----------------
2326

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Simplifying Updates with Reducers 🚧
2+
====================================
3+
4+
.. note::
5+
6+
Under construction 🚧

docs/source/reference-material/hooks-api.rst

+21-3
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,9 @@ There are **three important subtleties** to note about using asynchronous effect
186186
Manual Effect Conditions
187187
........................
188188

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

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

199199

200+
Use Context
201+
-----------
202+
203+
.. code-block::
204+
205+
value = use_context(MyContext)
206+
207+
Accepts a context object (the value returned from
208+
:func:`idom.core.hooks.create_context`) and returns the current context value for that
209+
context. The current context value is determined by the ``value`` argument passed to the
210+
nearest ``MyContext`` in the tree.
211+
212+
When the nearest <MyContext.Provider> above the component updates, this Hook will
213+
trigger a rerender with the latest context value passed to that MyContext provider. Even
214+
if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen
215+
starting at the component itself using useContext.
216+
217+
200218
Supplementary Hooks
201219
===================
202220

src/idom/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from .core.dispatcher import Stop
55
from .core.events import EventHandler, event
66
from .core.hooks import (
7+
create_context,
78
use_callback,
9+
use_context,
810
use_effect,
911
use_memo,
1012
use_reducer,
@@ -28,6 +30,7 @@
2830
"Component",
2931
"ComponentType",
3032
"config",
33+
"create_context",
3134
"event",
3235
"EventHandler",
3336
"hooks",
@@ -42,6 +45,7 @@
4245
"run",
4346
"Stop",
4447
"use_callback",
48+
"use_context",
4549
"use_effect",
4650
"use_memo",
4751
"use_reducer",

src/idom/core/_thread_local.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from threading import Thread, current_thread
2+
from typing import Callable, Generic, TypeVar
3+
from weakref import WeakKeyDictionary
4+
5+
6+
_StateType = TypeVar("_StateType")
7+
8+
9+
class ThreadLocal(Generic[_StateType]):
10+
"""Utility for managing per-thread state information"""
11+
12+
def __init__(self, default: Callable[[], _StateType]):
13+
self._default = default
14+
self._state: WeakKeyDictionary[Thread, _StateType] = WeakKeyDictionary()
15+
16+
def get(self) -> _StateType:
17+
thread = current_thread()
18+
if thread not in self._state:
19+
state = self._state[thread] = self._default()
20+
else:
21+
state = self._state[thread]
22+
return state
23+
24+
def set(self, state: _StateType) -> None:
25+
self._state[current_thread()] = state

src/idom/core/component.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
def component(
1111
function: Callable[..., Union[ComponentType, VdomDict | None]]
12-
) -> Callable[..., "Component"]:
12+
) -> Callable[..., Component]:
1313
"""A decorator for defining a new component.
1414
1515
Parameters:
@@ -54,6 +54,9 @@ def __init__(
5454
def render(self) -> VdomDict | ComponentType | None:
5555
return self.type(*self._args, **self._kwargs)
5656

57+
def should_render(self, new: Component) -> bool:
58+
return True
59+
5760
def __repr__(self) -> str:
5861
try:
5962
args = self._sig.bind(*self._args, **self._kwargs).arguments

0 commit comments

Comments
 (0)