|
| 1 | +import {State} from 'js-framework-benchmark-utils'; |
1 | 2 | import { |
2 | 3 | neverland as $, html, render, |
3 | | - useCallback, |
4 | | - useMemo, |
5 | | - useReducer |
6 | | -} from '../node_modules/neverland/esm/index.js'; |
7 | | - |
8 | | -import {Scope, listReducer} from './utils.js'; |
9 | | - |
| 4 | + useCallback, useMemo, useReducer |
| 5 | +} from 'neverland'; |
10 | 6 |
|
11 | 7 | const GlyphIcon = () => html`<span class="glyphicon glyphicon-remove" aria-hidden="true" />`; |
12 | 8 |
|
13 | | -const Row = $(({item, dispatch, selected}) => { |
14 | | - const select = useCallback(() => dispatch({type: 'select', id: item.id}), [item]), |
15 | | - remove = useCallback(() => dispatch({type: 'delete', id: item.id}), [item]); |
| 9 | +const Row = $(({data, item, dispatch, selected}) => { |
| 10 | + const {id, label} = item; |
| 11 | + |
| 12 | + const select = useCallback(() => dispatch({type: 'select', id}), [id]), |
| 13 | + remove = useCallback(() => dispatch({type: 'remove', id}), [id]); |
16 | 14 |
|
17 | | - return html.for(item)` |
| 15 | + return html.for(data, id)` |
18 | 16 | <tr class=${selected ? "danger" : ""}> |
19 | | - <td class="col-md-1">${item.id}</td> |
20 | | - <td class="col-md-4"><a onclick=${select}>${item.label}</a></td> |
| 17 | + <td class="col-md-1">${id}</td> |
| 18 | + <td class="col-md-4"><a onclick=${select}>${label}</a></td> |
21 | 19 | <td class="col-md-1"><a onclick=${remove}>${GlyphIcon()}</a></td> |
22 | 20 | <td class="col-md-6" /> |
23 | 21 | </tr> |
@@ -50,16 +48,38 @@ const Jumbotron = ({dispatch}) => html` |
50 | 48 | </div> |
51 | 49 | `; |
52 | 50 |
|
| 51 | +// This is a bit awkward, but also necessary because all methods in the State |
| 52 | +// are self-bound, meaning these will refer to an older state when changes happen. |
| 53 | +// With this update, the list reducer will refer always |
| 54 | +// to the updated object instead of the previously shallow copied one. |
| 55 | +let state = State($ => {state = $}); |
| 56 | + |
| 57 | +// As result, the listReducer will always return a shallow copy of the initial state, |
| 58 | +// instead of returning a copy of the previous shallow copy that won't be referenced. |
| 59 | +const listReducer = (_, action) => { |
| 60 | + const {type} = action; |
| 61 | + switch (type) { |
| 62 | + case 'remove': |
| 63 | + case 'select': |
| 64 | + state[type](action.id); |
| 65 | + break; |
| 66 | + default: |
| 67 | + state[type](); |
| 68 | + break; |
| 69 | + } |
| 70 | + return {...state}; |
| 71 | +}; |
| 72 | + |
53 | 73 | const Main = $(() => { |
54 | | - const [state, dispatch] = useReducer(listReducer, Scope); |
| 74 | + const [{data, selected}, dispatch] = useReducer(listReducer, state); |
55 | 75 | const jumbotron = useMemo(() => Jumbotron({dispatch}), []); |
56 | 76 |
|
57 | 77 | return html` |
58 | 78 | <div class="container"> |
59 | 79 | ${jumbotron} |
60 | 80 | <table class="table table-hover table-striped test-data"> |
61 | 81 | <tbody> |
62 | | - ${state.data.map(item => Row({item, dispatch, selected: item.id === state.selected}))} |
| 82 | + ${data.map(item => Row({data, item, dispatch, selected: item.id === selected}))} |
63 | 83 | </tbody> |
64 | 84 | </table> |
65 | 85 | <span class="preloadicon glyphicon glyphicon-remove" aria-hidden="true" /> |
|
0 commit comments