Skip to content

Commit

Permalink
tweaks to docs
Browse files Browse the repository at this point in the history
  • Loading branch information
brainkim committed Jul 13, 2020
1 parent 1dff8b0 commit a450d4f
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 47 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -10,7 +10,7 @@ Crank uses the same JSX syntax and diffing algorithm popularized by React, allow
### Just Functions
All components in Crank are just functions or generator functions. No classes, hooks, proxies or template languages are needed.

### Promise-fluent
### Native Promise Support
Crank provides first-class support for promises. You can use async/await directly in components, and race async components to display fallback UIs.

### Lightweight
Expand Down
10 changes: 5 additions & 5 deletions website/build.tsx
Expand Up @@ -197,7 +197,7 @@ function Home(): Element {
</header>
<main class="features">
<div class="feature">
<h3>Declarative components</h3>
<h3>Declarative Components</h3>
<p>
Crank uses the same JSX syntax and diffing algorithm popularized
by React, allowing you to write HTML-like code directly in your
Expand All @@ -212,11 +212,11 @@ function Home(): Element {
</p>
</div>
<div class="feature">
<h3>Promises today</h3>
<h3>Native Promise Support</h3>
<p>
Crank provides first-class support for promises. You can use
async/await directly in components, and race components to display
fallback UIs.
Crank provides first-class support for promises. You can use async
functions as components, and race components to display fallback
UIs.
</p>
</div>
</main>
Expand Down
File renamed without changes.
4 changes: 0 additions & 4 deletions website/guides/06-lifecycles.md
Expand Up @@ -4,10 +4,6 @@ title: Lifecycles

Crank uses generator functions rather than hooks or classes to define component lifecycles. Internally, this is achieved by calling the `next`, `return` and `throw` methods of the generator object as components are inserted, updated and removed from the element tree. As a developer, you can use the `yield`, `return`, `try`, `catch`, and `finally` keywords within your generator components to take full advantage of the generator’s natural lifecycle.

## A review of generator functions

TODO

## Returning from a generator

Usually, you’ll yield in generator components so that they can continue to respond to updates, but you may want to also `return` a final state. Unlike function components, which are called and returned once for each update, once a generator component returns, it will never update again.
Expand Down
4 changes: 2 additions & 2 deletions website/guides/07-reusable-logic.md
Expand Up @@ -250,7 +250,7 @@ async function *Counter() {
- Uniform logic to dispose of resources.

**Cons:**
- Promises and async iterators can be prone to race conditions and deadlocks.
- Promises and async iterators can cause race conditions and deadlocks, without any language-level features to help you debug them.

**Use cases:**
If you use async iterators/generators already, Crank is the perfect fit for your application.
If you use async iterators/generators already, Crank is the perfect framework for your application.
1 change: 0 additions & 1 deletion website/guides/08-working-with-typescript.md
Expand Up @@ -124,4 +124,3 @@ function MyButton (props) {

## Typing provisions
By default, calls to `Context.prototype.get` and `Context.prototype.set` will be loosely typed. If you want stricter typings of these methods, you can use module augmentation to extend the `ProvisionMap` interface provided by Crank.

@@ -1,11 +1,11 @@
---
title: Differences from React
title: Mapping React to Crank
---

Though Crank is very much inspired by and similar to React, exact compatibility is a non-goal, and we’ve used this as opportunity to “fix” a lot of pain points with React which bothered us over the years. The following is a list of differences with React, as well as justifications for why we chose to implement features differently.
Though Crank is inspired by React, compatibility is a non-goal, and certain concepts may be implemented using different, non-compatible APIs. The following is a reference for React developers to help them map React concepts APIs to their equivalents in Crank.

## No classes
Crank uses functions, generator functions and async functions to implement all of what React implements with classes. Here for instance, is the entirety of the React class-based API implemented with a single async generator function:
## Class Components
Crank uses functions exclusively for components; it does not provide a class-based component API. You can emulate most of the React Component class API with the natural lifecycle of an async generator function:

```jsx
async function *ReactComponent(props) {
Expand All @@ -19,6 +19,8 @@ async function *ReactComponent(props) {
ref = yield render(props, state);
state = componentDidUpdate(props, newProps, state, ref);
props = newProps;
} else {
yield <Copy />;
}
}
} catch (err) {
Expand All @@ -29,37 +31,68 @@ async function *ReactComponent(props) {
}
```

## No hooks
Crank does not implement hooks. Hooks bad.
This is pseudocode which demonstrates where code goes in an async generator component, compared to React class methods. Refer to the [guide on lifecycles](./lifecycles) for more information on using generator functions.

## No `setState` or `useState`
React has always tightly coupled component updates with local state. Because Crank uses generator functions, state is just local variables, and you can call `this.refresh()` to update the UI to match state. Decoupling these two concerns allows for more nuanced updates to local state without `shouldComponentUpdate` hacks, and is easier to reason about than relying on the framework to provide local state.
### `setState`
Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](#TTKTKTKTKTKTKTK) for examples.

## No `Suspense`
The project known as React `Suspense` is both sub-optimal and likely to be vaporware. It relies on the unusual mechanism of throwing promises, has the hard requirement of a caching mechanism, and is generally difficult to reason about. By leveraging async functions and async generators, Crank allows you to implement the `Suspense` element in user space. No argument from the React team about the necessity of `Suspense` will ever justify it over the convenience provided by being able to use the `await` operator directly in components.
### `shouldComponentUpdate`
Components themselves do not provide a way to prevent updates to themselves. Instead, you can use `Copy` elements to prevent the rerendering of a specific subtree. [Refer to the description of `Copy` elements](#TTKTKTK) for more information.

## Props match HTML attributes rather than JS APIs
### `for` not `htmlFor`, `class` not `className`
### `componentWillMount`/`componentDidMount`

Crank does not place any restrictions on the names of JSX props. This means that you can write JSX like `<label class="my-label" for="my-id">Label</label>`.
## style can be a `cssText` string, style object uses snake-case, and `px` is not magically added to numbers.
### getDerivedStateFromProps`/`componentWillUpdate`/`componentDidUpdate`/`getSnapshotBeforeUpdate`

### `componentWillUnmount`

### `forceUpdate`

### `defaultProps`

### Error Boundaries/`componentDidCatch`

## Hooks
Crank does not implement any APIs similar to React Hooks. The following are alternatives to specific hooks.

### `useState`/`useReducer`
Crank uses generator functions and local variables for local state. Refer to [the section on stateful components](#TTKTKTKTKTKTKTK).

### `useEffect`/`useLayoutEffect`
Crank makes no distinction between

### `useMemo`/`useCallback`

### Custom Hooks
The main appeal of hooks for library authors is that you can encapsulate shared logic in hooks. Refer to the [guide on reusable logic](./reusable-logic) for some patterns for reusing logic between components.

## `componentDidUpdate` and `React.memo`
Crank uses the special `Copy` element to prevent child updates.

## Suspense
Crank does not implement any sort of Suspense-like API. As an alternative, you can use async functions and async generator functions directly. See the [guide on async components](./async-components) for an introduction to async components, as well as a demonstration of how you can implement the `Suspense` component directly in user space.

## `React.cloneElement`
Elements are just plain-old JavaScript objects, and there is no need to use a special method to copy them. You can re-use the same elements within generators, mutate them, or use spread syntax to shallowly copy them. Crank will handle reused elements gracefully.

## DOM props
### `htmlFor` and `className`
Crank does not place any restrictions on the names of props. This means that you can write JSX like `<label class="my-label" for="my-id">Label</label>`.

### The `style` prop.
```jsx
<div style="color: red"><span style={{"font-size": "16px"}}>Hello</span></div>
```
## No “controlled”/“uncontrolled”, “value”/“defaultValue” components.
Styles can be

## “controlled”/“uncontrolled” or “value”/“defaultValue” props
If you don’t want your component to be updated, don’t update it.
## No `dangerouslySetInnerHTML={{__html}}` props.

## `dangerouslySetInnerHTML={{__html}}`
Just use the `innerHTML` prop. React doesn’t do anything to make setting `innerHTML` safe; they just make you type more and search for the exact spelling of `dangerouslySetInnerHTML` every month or so.
## Fragments can have `innerHTML`.
TKTKTK update for Raw
Fragment behavior can be overridden by renderers, and both the DOM and HTML renderers allow fragments to accept an innerHTML prop, allowing arbitrarily HTML to be written without a parent.
## Portals are just a special element tag.

## `ReactDOM.createPortal`
Their behavior is defined by renderers, and all element trees are wrapped implicitly or explicitly in a root portal.
### No `componentDidUpdate` or `React.memo`.
Crank uses the special `Copy` element to prevent child updates.
## No `React.cloneElement`
Elements are just plain-old JavaScript objects, and there is no need to use a special method to copy them. You can re-use the same elements within generators, mutate them, or use spread syntax to shallowly copy them. Crank will handle reused elements gracefully.
## No event props

Event props in React are terrible for the following reasons:
The EventTarget API takes more than just a function, it also takes options which allow you to register event listeners in the `capture` phase, register `passive` listeners, or register event listeners `once`. React has attempted to allow event handlers to be registered in the capture phase by adding props like `onClickCapture`, but embedding all these options in the prop name would be madness (`onClickCaptureOncePassive`). By emulating the event target API, Crank provides the full power of the `EventTarget` API.
## Stop doxxing event listeners.
Expand All @@ -68,16 +101,18 @@ Event listeners are difficult to name and make the most sense as anonymous funct
When attempting to define custom event handler props in React, React developers will typically mimic the component props API and allow callbacks to be passed into the component, which the component author will then call directly when they want to trigger the event. This is a painful to do, because you often have to make sure the callback is defined on props, because they are usually optional, and then React developers will also arbitrarily pass data to the callback which is not an event, making custom `onWhatever` props disanalogous with DOM event props, because DOM event props call callbacks with an event. There is no standard for what event-like callback props are called with in React, and there is no way for components to allow parents to prevent default behavior by calling `ev.preventDefault` as you would with a regular DOM event. Worst of all, these props must be passed directly from parent to child, so if a component wants to listen to an event in a deeply nested component, event handlers must either be passed using React contexts, or passed explicitly through each component layer, at each layer renamed to make sense for each nested component API.

Crank avoids this sitution by mimicking the DOM EventTarget API, and allowing developers to create and bubble real `Event` or `CustomEvent` objects with `this.dispatchEvent`. These events can be namespaced, typed, and components can allow parents to cancel events.
## No refs

## Refs
React’s `ref` API has undergone multiple revisions over the years and it’s only gotten more confusing/difficult to use. Crank passes rendered DOM nodes and strings back into generator functions, so you can access them by reading the result of `yield` expressions in generators. You can assign these “refs” to local variables and treat them as you would any local variable without worry.
## Children can contain any kind of iterable, not just arrays.

## Arrays in children vs iterables
There’s no reason to restrict children in JSX elements to just arrays. You can interpolate ES6 Maps, Sets or any other user-defined iterable into your Crank elements, and Crank will simply render them in an implicit `Fragment`.

## Keys
### key has been named to `crank-key`.
In React, the `key` prop is special and erased from the props visible to components. Insofar as `key` is a common word, Crank namespaces `key` as `crank-key`.
### The elements of iterables don’t require unique keys.
Pestering the user to add unique keys to every element of an array is not something Crank will ever do, insofar as most of the time, developers just set the `key` to an `index` into the array. If the developer needs state to be preserved, they will eventually discover that it isn’t preserved in the course of normal development and add a key.
## No render props
Crank strongly discourages the React idiom of passing a function as children (or any other prop for that matter. “Render props” produce ugly and non-sensical syntax, and were a direct response to the lack of composability of React class components. Most if not all “render prop” patterns are easier to implement in Crank with the use of higher-order components which are passed component functions and props and produce elements of their own accord.
## Contexts
### The elements of iterables in the element tree do not require unique keys.
Pestering the user to add unique keys to every element of an array is not something Crank will ever do, insofar as most of the time, developers just set the `key` to the current `index` of the item. If the developer needs state to be preserved, they will eventually discover that it isn’t preserved in the course of normal development and add a key.

## React Contexts
A feature equivalent to React Contexts is planned but not yet implemented.
5 changes: 3 additions & 2 deletions website/src/index.css
Expand Up @@ -247,19 +247,20 @@ li {

pre {
padding: .8em;
background: #e1e4ea;
margin: 1.5em 0;
font-size: .9rem;
background: #e1e4ea;
color: #0b2f5b;
overflow: auto;
tab-size: 2;
}

:not(pre) > code {
background: #2b303c;
padding: 0 .1em;
margin: 0;
display: inline;
background: #e1e4ea;
color: #0b2f5b;
}

.token.comment,
Expand Down

0 comments on commit a450d4f

Please sign in to comment.