Skip to content

Commit

Permalink
final pass
Browse files Browse the repository at this point in the history
  • Loading branch information
brainkim committed Jul 28, 2020
1 parent 5ec94fb commit 14c79a5
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 112 deletions.
8 changes: 4 additions & 4 deletions website/guides/02-elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const el = <div id="element">An element</div>;
const el1 = createElement("div", {id: "element"}, "An element");
```

The `createElement` function returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, Crank provides special classes called *renderers* which interpret elements to produce DOM nodes, HTML strings, WebGL scene graphs, or whatever else you can think of.
The `createElement` function provided by Crank returns an *element*, a JavaScript object. Elements on their own don’t do anything special; instead, we use special classes called *renderers* to interpret elements and produce DOM nodes, HTML strings, WebGL-backed scene graphs, or whatever else you can think of.

Crank ships with two renderer subclasses for web development: one for managing DOM nodes, available through the module `@bikeshaving/crank/dom`, and one for creating HTML strings, available through the module `@bikeshaving/crank/html`. You can use these modules to render interactive user interfaces in the browser and HTML responses on the server.

Expand Down Expand Up @@ -68,7 +68,7 @@ const el1 = createElement("div", {id: "my-id", "class": myClass});
console.log(el.props); // {id: "my-id", "class": "my-class"}
```

We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how we “pass” arguments into functions when invoking them.
We call this object the *props* object, short for “properties.” The value of each prop is a string if the string-like syntax is used (`key="value"`), or it can be an interpolated JavaScript expression by placing the value in curly brackets (`key={value}`). You can use props to “pass” values into host and component elements, similar to how you “pass” arguments into functions when invoking them.

If you already have an object that you want to use as props, you can use the special JSX `...` syntax to “spread” it into an element. This works similarly to [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).

Expand Down Expand Up @@ -107,7 +107,7 @@ renderer.render(el, document.body);
console.log(document.body.innerHTML); // <div>a2</div>
```

Crank also allows arbitrarily nested iterables of values to be interpolated as children, so, for instance, you can insert an array or a set of values into element trees.
Crank also allows arbitrarily nested iterables of values to be interpolated as children, so, for instance, you can insert arrays or sets of elements into element trees.

```jsx
const arr = [1, 2, 3];
Expand Down Expand Up @@ -141,4 +141,4 @@ console.log(document.body.firstChild === div); // true
console.log(document.body.firstChild.firstChild === span); // true
```

**Note:** We usually avoid using the term “virtual DOM” in Crank, insofar as the core renderer can be extended to target multiple environments; instead, we use the term “element diffing” to mean mostly the same thing.
**Note:** The documentation avoids the terms “virtual DOM” or “DOM diffing” insofar as the core renderer can be extended to target multiple environments; instead, we use the terms “virtual elements” and “element diffing” to mean mostly the same thing.
55 changes: 36 additions & 19 deletions website/guides/03-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function Greeting({name}) {
}

renderer.render(<Greeting name="World" />, document.body);
console.log(document.body.innerHTML); // "<div>Hello World</div>"
console.log(document.body.innerHTML);
// "<div>Hello World</div>"
```

Component elements can be passed children just as host elements can. The `createElement` function will add children to the props object under the name `children`, and it is up to the component to place these children somewhere in the returned element tree. If you don’t use the `children` prop, it will not appear in the rendered output.
Expand All @@ -34,7 +35,8 @@ renderer.render(
document.body,
);

console.log(document.body.innerHTML); // "<div>Message for Nemo: <span>Howdy</span></div>"
console.log(document.body.innerHTML);
// "<div>Message for Nemo: <span>Howdy</span></div>"
```

## Stateful Components
Expand Down Expand Up @@ -72,10 +74,10 @@ console.log(document.body.innerHTML);
// "<div>You have updated this component 1 time</div>"
```

By yielding elements rather than returning them, we can make components stateful using variables in the generator’s local scope. Every time a generator component is rendered, Crank resumes the generator, pausing at the next `yield`. The yielded expression, usually an element, is then recursively rendered, just as if it were returned from a function component. Furthermore, Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that the execution of generator components are preserved between renders.
By yielding elements rather than returning them, we can make components stateful using variables in the generator’s local scope. Crank uses the same diffing algorithm which reuses DOM nodes to reuse generator objects, so that their executions are preserved between renders. Every time a generator component is rendered, Crank resumes the generator and executes the generator until the next `yield`. The yielded expression, usually an element, is then rendered as the element’s children, just as if it were returned from a function component.

### Contexts
In the preceding example, the `Counter` component’s local state changes when it is rerendered, but we may want to write components which update themselves instead according to timers or events. Crank allows components to control themselves by passing in an object called a *context* as the `this` keyword of each component. Contexts provide several utility methods, most important of which is the `refresh` method, which tells Crank to update the related component instance in place.
In the preceding example, the `Counter` component’s local state changed when it was rerendered, but we may want to write components which update themselves according to timers or events instead. 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, most important of which is the `refresh` method, which tells Crank to update the related component instance in place.

```jsx
function *Timer() {
Expand All @@ -99,10 +101,10 @@ function *Timer() {

This `Timer` component is similar to the `Counter` one, except now the state (the local variable `seconds`) is updated in the callback passed to `setInterval`, 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.

One important detail about the `Timer` example is that it cleans up after itself with `clearInterval`. Crank will call the `return` method on generator components when the element is unmounted, so that the finally block executes and `clearInterval` is called. In this way, you can use the natural lifecycle of a generator to write setup and teardown logic for components, all within the same scope.
One important detail about the `Timer` example is that it cleans up after itself with `clearInterval` in the `finally` block. Crank will call the `return` method on an element’s related generator object when it is unmounted.

### Props Updates
The generator components we’ve seen so far haven’t used props. Generator components can accept props as its first parameter just like regular function components.
The generator components we’ve seen so far haven’t used props. Generator components can accept props as their first parameter just like regular function components.

```jsx
function *LabeledCounter({message}) {
Expand All @@ -113,21 +115,30 @@ function *LabeledCounter({message}) {
}
}

renderer.render(<LabeledCounter message="The count is now:" />, document.body);
renderer.render(
<LabeledCounter message="The count is now:" />,
document.body,
);

console.log(document.body.innerHTML); // "<div>The count is now: 1</div>"
renderer.render(<LabeledCounter message="The count is now:" />, document.body);

renderer.render(
<LabeledCounter message="The count is now:" />,
document.body,
);

console.log(document.body.innerHTML); // "<div>The count is now: 2</div>"

renderer.render(
<LabeledCounter message="What if I update the message:" />,
<LabeledCounter message="Le décompte est maintenant:" />,
document.body,
);

// WOOPS!
console.log(document.body.innerHTML); // "<div>The count is now: 3</div>"
```

This mostly works, except now we have a bug where the component kept yielding the initial message even though a new message was passed in via props. To fix this, we can make sure props are kept up to date by iterating over the context:
This mostly works, except we have a bug where the component keeps yielding elements with the initial message even though a new message was passed in via props. We can make sure props are kept up to date by iterating over the context:

```jsx
function *Counter({message}) {
Expand All @@ -140,21 +151,26 @@ function *Counter({message}) {
}
}

renderer.render(<Counter message="The count is now:" />, document.body);
renderer.render(
<Counter message="The count is now:" />,
document.body,
);

console.log(document.body.innerHTML); // "<div>The count is now: 1</div>"

renderer.render(
<Counter message="What if I update the message:" />,
<Counter message="Le décompte est maintenant:" />,
document.body,
);
console.log(document.body.innerHTML);
// "<div>What if I update the message: 2</div>"

console.log(document.body.innerHTML); // "<div>Le décompte est maintenant: 2</div>"
```

By replacing the `while` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to elements.
By replacing the `while` loop with a `for…of` loop which iterates over `this`, you can get the latest props each time the generator is resumed. This is possible because contexts are an iterable of the latest props passed to components.

### Comparing Old and New Props

One idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters with the 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. However, there is no requirement that you must always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old ones:
One Crank idiom we see in the preceding example is that we overwrite the variables declared via the generator’s parameters with the 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. However, there is no requirement that you must always overwrite old props in the `for` expression, meaning you can assign new props to a different variable and compare them against the old props.

```jsx
function *Greeting({name}) {
Expand Down Expand Up @@ -185,7 +201,7 @@ console.log(document.body.innerHTML); // "<div>Hello again Bob</div>"
The fact that state is just local variables allows us to blur the lines between props and state, in a way that is easy to understand and without lifecycle methods like `componentWillUpdate` from React. With generators and `for` loops, comparing old and new props is as easy as comparing adjacent elements of an array.

## Default Props
You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to a specific prop by using JavaScript’s default value syntax.
You may have noticed in the preceding examples that we used [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) on the props parameter for convenience. You can further assign default values to specific props by using JavaScript’s default value syntax.

```jsx
function Greeting({name="World"}) {
Expand All @@ -195,12 +211,13 @@ function Greeting({name="World"}) {
renderer.render(<Greeting />, document.body); // "<div>Hello World</div>"
```

This works well for function components, but for generator components, you should make sure that you use the same default value in both the parameter list and the `for` statement.
This syntax works well for function components, but for generator components, you should make sure that you use the same default value in both the parameter list and the `for` statement.

```jsx
function *Greeting({name="World"}) {
yield <div>Hello, {name}</div>;
for ({name="World"} of this) {
yield <div>Hello, {name}</div>;
yield <div>Hello again, {name}</div>;
}
}
```
Expand Down
Loading

0 comments on commit 14c79a5

Please sign in to comment.