Recast key docs to be much clearer and more accurate (#2540)
* Recast key docs to be much clearer and more accurate [skip ci] Also, fix a couple broken hash links I noticed while making this. * Ensure `diff` blocks are highlighted correctly
This commit is contained in:
parent
d4551f49f5
commit
512eef378e
8 changed files with 505 additions and 193 deletions
|
|
@ -109,7 +109,7 @@ function Layout() {
|
|||
}
|
||||
```
|
||||
|
||||
This would end up [throwing an error](keys.md#avoid-mixing-keyed-and-non-keyed-vnodes-in-the-same-array) because here's what Mithril sees when creating the `Layout` vnode:
|
||||
This would end up [throwing an error](keys.md#key-restrictions) because here's what Mithril sees when creating the `Layout` vnode:
|
||||
|
||||
```javascript
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Framework comparison
|
||||
|
||||
- [Why not X?](#why-not-insert-favorite-framework-here)
|
||||
- [Why use Mithril?](#why-use-mithril)
|
||||
- [Why not X?](#why-not-insert-favorite-framework-here?)
|
||||
- [Why use Mithril?](#why-use-mithril?)
|
||||
- [React](#react)
|
||||
- [Angular](#angular)
|
||||
- [Vue](#vue)
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ m.render(document.body, userInputs(users))
|
|||
|
||||
Having a key means that if the `users` array is shuffled and the view is re-rendered, the inputs will be shuffled in the exact same order, so as to maintain correct focus and DOM state.
|
||||
|
||||
To learn more about keys, [see the keys page](keys.md)
|
||||
To learn more about keys, [see the keys page](keys.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Introduction
|
||||
|
||||
- [What is Mithril?](#what-is-mithril)
|
||||
- [What is Mithril?](#what-is-mithril?)
|
||||
- [Getting started](#getting-started)
|
||||
- [Hello world](#hello-world)
|
||||
- [DOM elements](#dom-elements)
|
||||
|
|
|
|||
649
docs/keys.md
649
docs/keys.md
File diff suppressed because one or more lines are too long
|
|
@ -3,6 +3,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<title>Mithril.js</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/themes/prism.min.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
|
@ -27,8 +28,9 @@
|
|||
<small>License: MIT. © Leo Horie.</small>
|
||||
</section>
|
||||
</main>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js" defer></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-jsx.min.js" defer></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/prism.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/components/prism-jsx.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/components/prism-diff.min.js"></script>
|
||||
<script src="https://unpkg.com/mithril@[version]/mithril.js" async></script>
|
||||
<script>
|
||||
document.querySelector(".hamburger").onclick = function() {
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ Argument | Type | Description
|
|||
`vnode.attrs` | `Object` | A map of URL parameter values
|
||||
**returns** | `Array<Vnode>|Vnode` | The [vnodes](vnodes.md) to be rendered
|
||||
|
||||
The `vnode` parameter is just `m(Component, m.route.param())` where `Component` is the resolved component for the route (after `routeResolver.onmatch`) and `m.route.param()` is as documented [here](#mrouteparam). If you omit this method, the default return value is `[vnode]`, wrapped in a fragment so you can use [key parameters](#key-parameter). Combined with a `:key` parameter, it becomes a [single-element keyed fragment](keys.md#single-child-keyed-fragments), since it ends up rendering to something like `[m(Component, {key: m.route.param("key"), ...})]`.
|
||||
The `vnode` parameter is just `m(Component, m.route.param())` where `Component` is the resolved component for the route (after `routeResolver.onmatch`) and `m.route.param()` is as documented [here](#mrouteparam). If you omit this method, the default return value is `[vnode]`, wrapped in a fragment so you can use [key parameters](#key-parameter). Combined with a `:key` parameter, it becomes a [single-element keyed fragment](keys.md#reinitializing-views-with-single-child-keyed-fragments), since it ends up rendering to something like `[m(Component, {key: m.route.param("key"), ...})]`.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -403,7 +403,7 @@ It's possible to have multiple arguments in a route, for example `/edit/:project
|
|||
|
||||
When a user navigates from a parameterized route to the same route with a different parameter (e.g. going from `/page/1` to `/page/2` given a route `/page/:id`, the component would not be recreated from scratch since both routes resolve to the same component, and thus result in a virtual dom in-place diff. This has the side-effect of triggering the `onupdate` hook, rather than `oninit`/`oncreate`. However, it's relatively common for a developer to want to synchronize the recreation of the component to the route change event.
|
||||
|
||||
To achieve that, it's possible to combine route parameterization with the virtual dom [key reconciliation](keys.md) feature:
|
||||
To achieve that, it's possible to combine route parameterization with [keys](#reinitializing-views-with-single-child-keyed-fragments) for a very convenient pattern:
|
||||
|
||||
```javascript
|
||||
m.route(document.body, "/edit/1", {
|
||||
|
|
@ -421,7 +421,7 @@ Or even use the [`history state`](#history-state) feature to achieve reloadable
|
|||
|
||||
`m.route.set(m.route.get(), null, {state: {key: Date.now()}})`
|
||||
|
||||
Note that the key parameter works only for component routes. If you're using a route resolver, you'll need to use a [single-child keyed fragment](keys.md), passing `key: m.route.param("key")`, to accomplish the same.
|
||||
Note that the key parameter works only for component routes. If you're using a route resolver, you'll need to use a [single-child keyed fragment](keys.md#reinitializing-views-with-single-child-keyed-fragments), passing `key: m.route.param("key")`, to accomplish the same.
|
||||
|
||||
#### Variadic routes
|
||||
|
||||
|
|
|
|||
|
|
@ -113,3 +113,30 @@ When creating libraries that emit vnodes, you should use this module instead of
|
|||
Vnodes are supposed to represent the state of the DOM at a certain point in time. Mithril's rendering engine assumes a reused vnode is unchanged, so modifying a vnode that was used in a previous render will result in undefined behavior.
|
||||
|
||||
It is possible to reuse vnodes to prevent a diff, but it's preferable to use the `onbeforeupdate` hook to make your intent clear to other developers (or your future self).
|
||||
|
||||
#### Avoid passing model data directly to components via attributes
|
||||
|
||||
The `key` property may appear in your data model in a way that conflicts with Mithril's key logic, and your model might itself be a mutable instance with a method that shares a name with a lifecycle hook like `onupdate` or `onremove`. For example, a model might use a `key` property to represent a customizable color key. When this changes, it can lead to components receiving wrong data, changing positions unexpectedly, or other unexpected, unwanted behavior. Instead, pass it as an attribute so Mithril doesn't misinterpret it (and so you still can potentially mutate it or call prototype methods on it later on):
|
||||
|
||||
```javascript
|
||||
// Data model
|
||||
var users = [
|
||||
{id: 1, name: "John", key: 'red'},
|
||||
{id: 2, name: "Mary", key: 'blue'},
|
||||
]
|
||||
|
||||
// Later on...
|
||||
users[0].key = 'yellow'
|
||||
|
||||
// AVOID
|
||||
users.map(function(user){
|
||||
// The component for John will be destroyed and recreated
|
||||
return m(UserComponent, user)
|
||||
})
|
||||
|
||||
// PREFER
|
||||
users.map(function(user){
|
||||
// Key is specifically extracted: data model is given its own property
|
||||
return m(UserComponent, {key: user.id, model: user})
|
||||
})
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue