|
1 |
| -import { memo, FC } from "react"; |
| 1 | +import React, { memo, FC } from "react"; |
2 | 2 | import ReactDOM from "react-dom";
|
3 |
| -import { atom, useAtom } from "jotai"; |
| 3 | +import { atom, useAtom, PrimitiveAtom } from "jotai"; |
4 | 4 | import { useUpdateAtom } from "jotai/utils";
|
5 | 5 |
|
6 |
| -import { buildData, Data } from "./utils"; |
| 6 | +import { Data, buildDataAtoms } from "./utils"; |
7 | 7 |
|
8 |
| -const stateAtom = atom<{ data: Data[]; selected: number }>({ |
9 |
| - data: [], |
10 |
| - selected: 0, |
11 |
| -}); |
| 8 | +const dataAtom = atom<PrimitiveAtom<Data>[]>([]); |
| 9 | +const selectedAtom = atom<PrimitiveAtom<Data> | null>(null); |
12 | 10 |
|
13 |
| -const createRowsAtom = atom(null, (_, set, amount: number) => |
14 |
| - set(stateAtom, { data: buildData(amount), selected: 0 }) |
15 |
| -); |
| 11 | +const createRowsAtom = atom(null, (_, set, amount: number) => { |
| 12 | + set(dataAtom, buildDataAtoms(amount)); |
| 13 | + set(selectedAtom, null); |
| 14 | +}); |
16 | 15 |
|
17 |
| -const appendRowsAtom = atom(null, (_, set) => |
18 |
| - set(stateAtom, (state) => ({ |
19 |
| - data: state.data.concat(buildData(1000)), |
20 |
| - selected: 0, |
21 |
| - })) |
22 |
| -); |
| 16 | +const appendRowsAtom = atom(null, (_, set) => { |
| 17 | + set(dataAtom, (data) => data.concat(buildDataAtoms(1000))); |
| 18 | + set(selectedAtom, null); |
| 19 | +}); |
23 | 20 |
|
24 |
| -const updateRowsAtom = atom(null, (_, set) => |
25 |
| - set(stateAtom, ({ data, selected }) => { |
26 |
| - const newData = data.slice(0); |
27 |
| - for (let i = 0; i < newData.length; i += 10) { |
28 |
| - const r = newData[i]; |
29 |
| - newData[i] = { id: r.id, label: r.label + " !!!" }; |
30 |
| - } |
31 |
| - return { data: newData, selected }; |
32 |
| - }) |
33 |
| -); |
| 21 | +const updateRowsAtom = atom(null, (get, set) => { |
| 22 | + const data = get(dataAtom); |
| 23 | + for (let i = 0; i < data.length; i += 10) { |
| 24 | + set(data[i], (r) => ({ id: r.id, label: r.label + " !!!" })); |
| 25 | + } |
| 26 | +}); |
34 | 27 |
|
35 |
| -const removeRowAtom = atom(null, (_, set, id: number) => |
36 |
| - set(stateAtom, ({ data, selected }) => { |
37 |
| - const idx = data.findIndex((d) => d.id === id); |
38 |
| - return { data: [...data.slice(0, idx), ...data.slice(idx + 1)], selected }; |
| 28 | +const removeRowAtom = atom(null, (_, set, item: PrimitiveAtom<Data>) => |
| 29 | + set(dataAtom, (data) => { |
| 30 | + const idx = data.findIndex((d) => d === item); |
| 31 | + return [...data.slice(0, idx), ...data.slice(idx + 1)]; |
39 | 32 | })
|
40 | 33 | );
|
41 | 34 |
|
42 |
| -const selectRowAtom = atom(null, (_, set, selected: number) => |
43 |
| - set(stateAtom, (state) => ({ data: state.data, selected })) |
44 |
| -); |
| 35 | +const selectRowAtom = atom(null, (_, set, selected: PrimitiveAtom<Data>) => { |
| 36 | + set(selectedAtom, selected); |
| 37 | +}); |
45 | 38 |
|
46 |
| -const clearStateAtom = atom(null, (_, set) => |
47 |
| - set(stateAtom, () => ({ |
48 |
| - data: [], |
49 |
| - selected: 0, |
50 |
| - })) |
51 |
| -); |
| 39 | +const clearStateAtom = atom(null, (_, set) => { |
| 40 | + set(dataAtom, []); |
| 41 | + set(selectedAtom, null); |
| 42 | +}); |
52 | 43 |
|
53 |
| -const swapRowsAtom = atom(null, (_, set) => |
54 |
| - set(stateAtom, (state) => { |
55 |
| - const { data, selected } = state; |
56 |
| - return data.length > 998 |
57 |
| - ? { |
58 |
| - data: [data[0], data[998], ...data.slice(2, 998), data[1], data[999]], |
59 |
| - selected, |
60 |
| - } |
61 |
| - : state; |
62 |
| - }) |
63 |
| -); |
| 44 | +const swapRowsAtom = atom(null, (get, set) => { |
| 45 | + const data = get(dataAtom); |
| 46 | + if (data.length > 998) { |
| 47 | + set(dataAtom, [data[0], data[998], ...data.slice(2, 998), data[1], data[999]]) |
| 48 | + } |
| 49 | +}); |
64 | 50 |
|
65 | 51 | const GlyphIcon = (
|
66 | 52 | <span className="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
67 | 53 | );
|
68 | 54 |
|
69 | 55 | interface RowProps {
|
70 |
| - id: number; |
71 |
| - label: string; |
| 56 | + item: PrimitiveAtom<Data>; |
72 | 57 | isSelected: boolean;
|
73 |
| - selectRow: (id: number) => void; |
74 |
| - removeRow: (id: number) => void; |
75 | 58 | }
|
76 | 59 |
|
77 |
| -const Row = memo<RowProps>(({ id, label, isSelected, selectRow, removeRow }) => { |
| 60 | +const Row = memo<RowProps>(({ item, isSelected }) => { |
| 61 | + const [{ id, label }] = useAtom(item); |
| 62 | + const selectRow = useUpdateAtom(selectRowAtom); |
| 63 | + const removeRow = useUpdateAtom(removeRowAtom); |
78 | 64 | return (
|
79 | 65 | <tr className={isSelected ? "danger" : ""}>
|
80 | 66 | <td className="col-md-1">{id}</td>
|
81 | 67 | <td className="col-md-4">
|
82 |
| - <a onClick={() => selectRow(id)}>{label}</a> |
| 68 | + <a onClick={() => selectRow(item)}>{label}</a> |
83 | 69 | </td>
|
84 | 70 | <td className="col-md-1">
|
85 |
| - <a onClick={() => removeRow(id)}>{GlyphIcon}</a> |
| 71 | + <a onClick={() => removeRow(item)}>{GlyphIcon}</a> |
86 | 72 | </td>
|
87 | 73 | <td className="col-md-6"></td>
|
88 | 74 | </tr>
|
89 | 75 | );
|
90 | 76 | });
|
91 | 77 |
|
92 |
| -interface RowListProp { |
93 |
| - selectRow: (id: number) => void; |
94 |
| - removeRow: (id: number) => void; |
95 |
| -} |
96 | 78 |
|
97 |
| -const RowList = memo<RowListProp>(({ selectRow, removeRow }) => { |
98 |
| - const [{ data, selected }] = useAtom(stateAtom); |
| 79 | +const RowList = memo(() => { |
| 80 | + const [data] = useAtom(dataAtom); |
| 81 | + const [selected] = useAtom(selectedAtom); |
99 | 82 | return (
|
100 | 83 | <>
|
101 | 84 | {data.map((item) => (
|
102 | 85 | <Row
|
103 |
| - key={item.id} |
104 |
| - id={item.id} |
105 |
| - label={item.label} |
106 |
| - isSelected={selected === item.id} |
107 |
| - selectRow={selectRow} |
108 |
| - removeRow={removeRow} |
| 86 | + key={String(item)} |
| 87 | + item={item} |
| 88 | + isSelected={item === selected} |
109 | 89 | />
|
110 | 90 | ))}
|
111 | 91 | </>
|
@@ -137,8 +117,7 @@ const Main: FC = () => {
|
137 | 117 | const updateRows = useUpdateAtom(updateRowsAtom);
|
138 | 118 | const clearState = useUpdateAtom(clearStateAtom);
|
139 | 119 | const swapRows = useUpdateAtom(swapRowsAtom);
|
140 |
| - const selectRow = useUpdateAtom(selectRowAtom); |
141 |
| - const removeRow = useUpdateAtom(removeRowAtom); |
| 120 | + |
142 | 121 | return (
|
143 | 122 | <div className="container">
|
144 | 123 | <div className="jumbotron">
|
@@ -172,7 +151,7 @@ const Main: FC = () => {
|
172 | 151 | </div>
|
173 | 152 | <table className="table table-hover table-striped test-data">
|
174 | 153 | <tbody>
|
175 |
| - <RowList selectRow={selectRow} removeRow={removeRow} /> |
| 154 | + <RowList /> |
176 | 155 | </tbody>
|
177 | 156 | </table>
|
178 | 157 | <span
|
|
0 commit comments