You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Identifiers like `createElement`, `Fragment` must be manually imported.
31
-
32
-
The automatic transform turns JSX elements into function calls from an automatically imported namespace.
30
+
With the classic transform, the `createElement` function must be manually imported. The automatic transform turns JSX elements into function calls from an automatically imported namespace.
The automatic transform has the benefit of not requiring manual imports. Beyond this fact, there is no difference between the two transforms, and the `_jsx()`/`_jsxs()` functions are wrappers around `createElement()`.
63
+
The automatic transform has the benefit of not requiring manual imports. Beyond this fact, there is no difference between the two transforms, and the provided `_jsx()`/`_jsxs()` functions are wrappers around `createElement()`.
66
64
67
65
## Renderers
68
66
69
-
Crank ships with two renderer subclasses for the web: one for managing DOM nodes in a front-end application, available through the module `@b9g/crank/dom`, and one for creating HTML strings, available through the module `@b9g/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
67
+
Crank provides two renderer subclasses for the web: one for managing DOM nodes in a front-end application, available through the module `@b9g/crank/dom`, and one for creating HTML strings, available through the module `@b9g/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.
Crank uses the same “virtual DOM” diffing algorithm made popular by React, where we compare elements by tag and position to reuse DOM nodes. This approach allows you to write declarative code which focuses on producing the right tree, while the framework does the dirty work of mutating the DOM efficiently.
175
+
Crank uses the same “virtual DOM” diffing algorithm made popular by React, where elements are compared by tag and position to reuse DOM nodes. This approach allows you to write declarative code which focuses on producing the right tree, while the framework does the dirty work of mutating the DOM efficiently.
Copy file name to clipboardexpand all lines: website/documents/guides/03-components.md
+12-9
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ title: Components
6
6
7
7
So far, we’ve only seen and used *host elements*. By convention, all host elements use lowercase tags like `<a>` or `<div>`, and these elements are rendered as their HTML equivalents.
8
8
9
-
However, eventually we’ll want to group these elements into reusable *components*. In Crank, components are defined with plain old JavaScript functions, including async and generator functions, which return or yield JSX elements. These functions can be referenced as element tags, and component elements are distinguished from host elements through the use of capitalized identifiers. The capitalized identifier is not just a convention but a way to tell JSX compilers to interpret the tag as an identifier rather than a literal string.
9
+
However, eventually we’ll want to group these elements into reusable *components*. In Crank, all components are defined with plain old JavaScript functionswhich produce JSX elements. These functions can be referenced as element tags, and component elements are distinguished from host elements through the use of capitalized identifiers. The capitalized identifier is not just a convention but a way to tell JSX compilers to interpret the tag as an identifier rather than a literal string.
10
10
11
11
The simplest kind of component is a *function component*. When rendered, the function is invoked with the props of the element as its first argument, and the return value of the function is rendered as the element’s children.
12
12
@@ -42,7 +42,7 @@ renderer.render(
42
42
);
43
43
```
44
44
45
-
The type of children is unknown, i.e. it could be an array, an element, or whatever else the caller passes in.
45
+
The type of children is unknown, e.g. it could be an array, an element, or whatever else the caller passes in.
46
46
47
47
## Stateful Components
48
48
Eventually, you’ll want to write components with local state. In Crank, we use [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) to do so. These types of components are referred to as *generator components*.
@@ -58,7 +58,7 @@ function *Counter() {
58
58
count++;
59
59
yield (
60
60
<div>
61
-
You have updated this component {count} time{count !==1&&"s"}.
61
+
This component has updated {count} time{count !==1&&"s"}.
By yielding elements rather than returning them, components can be made stateful using variables in the generator’s local scope. Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so there will only be one execution of a generator component for a given element in the tree.
73
73
74
74
## The Crank Context
75
-
In the preceding example, the component’s local state was updated directly when the generator was executed. This is of limited value insofar as what we usually want want is to update according to events or timers.
75
+
In the preceding example, the component’s local state `count`was updated directly when the generator was re-rendered. This is of limited value insofar as what we usually want want is to update according to events or timers.
76
76
77
77
Crank allows components to control their own execution by passing in an object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, the most important of which is the `refresh()` method, which tells Crank to update the related component instance in place.
This `<Timer>` component is similar to the `<Counter>` one, except now the state (the local variable `seconds`) is updated in a `setInterval()` callback, rather than when the component is rerendered. Additionally, the `refresh()` method is called to ensure that the generator is stepped through whenever the `setInterval()` callback fires, so that the rendered DOM actually reflects the updated `seconds` variable. Finally, the `<Timer>` component is passed a display message as a prop.
102
103
103
-
One important detail about the `Timer` example is that it cleans up after itself with `clearInterval()` in the `finally` block. Behind the scenes, Crank will call the `return()` method on an element’s generator object when it is unmounted.
104
+
One important detail about the `Timer` example is that it cleans up after itself with `clearInterval()` in the `finally` block. Behind the scenes, Crank will call the `return()` method on the component’s generator object when it is unmounted.
104
105
105
106
If you hate the idea of using the `this` keyword, the context is also passed in as the second parameter of components.
106
107
@@ -126,7 +127,7 @@ function *Timer({message}, ctx) {
126
127
127
128
## The Render Loop
128
129
129
-
The `<Timer>` component works, but it can be improved. Firstly, while the component is stateful, it would not update the message if it was rerendered with new props. Secondly, the `while (true)` loop can iterate infinitely if you forget to add a `yield`. To solve these issues, Crank contexts are an iterable of the latest props.
130
+
The `<Timer>` component works, but it can be improved. Firstly, while the component is stateful, it would not update the message if it was rerendered with new props. Secondly, the `while (true)` loop can iterate infinitely if you forget to add a `yield`, leading to unresponsive pages. To solve both of these issues, Crank contexts are themselves an iterable of props.
130
131
131
132
```jsx live
132
133
import {renderer} from"@b9g/crank/dom";
@@ -139,7 +140,7 @@ function *Timer({message}) {
139
140
140
141
for ({message} ofthis) {
141
142
yield (
142
-
<div>{message}: {seconds}</div>
143
+
<div>{message} {seconds}</div>
143
144
);
144
145
}
145
146
@@ -159,9 +160,9 @@ setTimeout(() => {
159
160
}, 2500);
160
161
```
161
162
162
-
The loop created by iterating over contexts is called the *render loop*. By replacing the `while` loop with a `for...of` loop, you can get the latest props each time the generator is resumed. It also provides benefits over `while` loops, like throwing errors if you forget to `yield`, and allowing you to write cleanup code after the loop without having to wrap the block in a `try`/`finally` block.
163
+
The loop created by iterating over contexts is called the *render loop*. By replacing the `while` loop with a `for...of` loop, you can get the latest props each time the generator is resumed. It also prevents common development mistakes by throwing errors if you forget to yield, or yield multiple times in a loop. FInally, it also allows you to write cleanup code after the loop without having to wrap the entire loop in a `try`/`finally` block, as you would in a `while` loop.
163
164
164
-
One Crank idiom you may have noticed is that we define props in function parameters, and overwrite them using a destructuring expression in the `for...of` statement. This is an easy way to make sure those variables stay in sync with the current props of the component. For this reason, even if your component has no props, it is idiomatic to destructure props and use a `for...of` loop.
165
+
One Crank idiom you may have noticed is that we define props in function parameters and overwrite them using a destructuring expression. This is an easy way to make sure those variables stay in sync with the current props of the component. For this reason, even if your component has no props, it is idiomatic to destructure props and use a `for...of` loop.
165
166
166
167
```jsx live
167
168
import {renderer} from"@b9g/crank/dom";
@@ -172,6 +173,8 @@ function *Counter() {
172
173
this.refresh();
173
174
};
174
175
176
+
// using an empty destructuring expression means we do not need to declare
Copy file name to clipboardexpand all lines: website/documents/guides/04-handling-events.md
+10-14
Original file line number
Diff line number
Diff line change
@@ -2,10 +2,10 @@
2
2
title: Handling Events
3
3
---
4
4
5
-
Most web applications require some measure of interactivity, where the user interface updates according to input. To facilitate this, Crank provides several ways to listen to and trigger events.
5
+
Most web applications require some measure of interactivity, where the user interface updates according to interactions like clicks and form inputs. To facilitate this, Crank provides several ways to listen to and trigger events.
6
6
7
7
## DOM Event Props
8
-
You can attach event callbacks to host element directly using event props. These props start with `on`, are by convention lowercase, and correspond to the event type (`onclick`, `onkeydown`). By combining event props, local variables and `this.refresh()`, you can write interactive components.
8
+
You can attach event callbacks to host element directly using event props. These props start with `on`, are lowercase, and correspond to the event type (`onclick`, `onkeydown`). By combining event props, local variables and `this.refresh()`, you can write interactive components.
9
9
10
10
```jsx live
11
11
import {renderer} from"@b9g/crank/dom";
@@ -28,6 +28,8 @@ function *Counter() {
28
28
renderer.render(<Counter />, document.body);
29
29
```
30
30
31
+
Camel-cased event props are supported for compatibility reasons starting in 0.6.
32
+
31
33
## The EventTarget Interface
32
34
As an alternative to event props, Crank contexts implement the same `EventTarget` interface used by the DOM. The `addEventListener()` method attaches a listener to a component’s root DOM node.
33
35
@@ -52,11 +54,7 @@ function *Counter() {
52
54
renderer.render(<Counter />, document.body);
53
55
```
54
56
55
-
The context’s `addEventListener()` method attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation.
56
-
57
-
While the `removeEventListener()` method is implemented, you do not have to call the `removeEventListener()` method if you merely want to remove event listeners when the component is unmounted.
58
-
59
-
Because the event listener is attached to the outer `div`, we have to filter events by `ev.target.tagName` in the listener to make sure we’re not incrementing `count` based on clicks which don’t target the `button` element.
57
+
The context’s `addEventListener()` method attaches to the top-level node or nodes which each component renders, so if you want to listen to events on a nested node, you must use event delegation. For instance, in the following example, we have to filter events by target to make sure we’re not incrementing `count` based on clicks to the outer `div`.
60
58
61
59
```jsx live
62
60
import {renderer} from"@b9g/crank/dom";
@@ -83,6 +81,8 @@ function *Counter() {
83
81
renderer.render(<Counter />, document.body);
84
82
```
85
83
84
+
While the `removeEventListener()` method is implemented, you do not have to call the `removeEventListener()` method if you merely want to remove event listeners when the component is unmounted.
85
+
86
86
## Dispatching Events
87
87
Crank contexts implement the full EventTarget interface, meaning you can use [the `dispatchEvent` method](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) and [the `CustomEvent` class](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) to dispatch custom events to ancestor components:
Using custom events and event bubbling allows you to encapsulate state transitions within component hierarchies without the need for complex state management solutions in a way that is DOM-compatible.
176
176
177
177
## Event props vs EventTarget
178
-
The props-based event API and the context-based EventTarget API both have their advantages. On the one hand, using event props means you can listen to exactly the element you’d like to listen to.
179
-
180
-
On the other hand, using the `addEventListener` method allows you to take full advantage of the EventTarget API, which includes registering passive event listeners, or listeners which are dispatched during the capture phase. Additionally, the EventTarget API can be used without referencing or accessing the child elements which a component renders, meaning you can use it to listen to elements nested in other components.
181
-
182
-
Crank supports both API styles for convenience and flexibility.
178
+
The props-based event API and the context-based EventTarget API both have their advantages. On the one hand, using event props means you can listen to exactly the element you’d like to listen to. On the other hand, using the `addEventListener` method allows you to take full advantage of the EventTarget API, which includes registering passive event listeners, or listeners which are dispatched during the capture phase. Crank supports both API styles for convenience and flexibility.
183
179
184
180
## Form Elements
185
181
186
-
Because Crank uses explicit state updates, it doesn’t need a concept like “controlled” `value` vs “uncontrolled” `defaultValue` props in React. No update means the value is uncontrolled.
182
+
Because Crank uses explicit state updates, it doesn’t require “controlled” or “uncontrolled” form props. No render means no update.
187
183
188
184
```jsx live
189
185
import {renderer} from"@b9g/crank/dom";
@@ -213,7 +209,7 @@ function *Form() {
213
209
renderer.render(<Form />, document.body);
214
210
```
215
211
216
-
If your component is updating for other reasons, you can use the special property `copy` to prevent the input element from updating.
212
+
If your component needs to update for other reasons, you can use the special `copy`prop to prevent the input element from updating. The `copy` prop is a boolean which prevents an element and children from rerendering.
0 commit comments