PL
React - jak stworzyć edytowalną tabelę
0 points
W tym krótkim artykule chcielibyśmy pokazać, jak stworzyć prostą edytowalną tabelę w Reakcie.

Uwaga:
Przejdź do tego artykułu, aby zobaczyć przykład edytowalnej komórki tabeli.
Praktyczny przykład:
xxxxxxxxxx
1
// Uwaga: Odkomentuj poniższe linijki podczas pracy z kompilatorem JSX:
2
// import React from 'react';
3
// import ReactDOM from 'react-dom';
4
5
// Komórka -----------------------------------------
6
7
const itemStyle = {
8
padding: '2px',
9
border: '1px solid silver',
10
fontFamily: 'Arial',
11
fontSize: '13px',
12
display: 'flex'
13
};
14
15
const textStyle = {
16
itemStyle,
17
padding: '5px 4px',
18
height: '16px'
19
};
20
21
const inputStyle = {
22
padding: '0',
23
flex: '1',
24
width: '100%',
25
minWidth: '20px',
26
fontFamily: 'Arial',
27
fontSize: '13px'
28
};
29
30
const buttonStyle = {
31
flex: 'none'
32
};
33
34
const Cell = React.memo(({value, mode, onChange}) => {
35
const [localMode, setLocalMode] = React.useState(mode ?? 'read');
36
const [localValue, setLocalValue] = React.useState(value ?? '');
37
React.useEffect(() => setLocalMode(mode ?? 'read'), [mode]);
38
React.useEffect(() => setLocalValue(value ?? ''), [value]);
39
if (localMode === 'edit') {
40
const handleInputChange = (e) => setLocalValue(e.target.value);
41
const handleSaveClick = () => {
42
setLocalMode('read');
43
onChange?.(localValue);
44
};
45
return (
46
<div style={itemStyle}>
47
<input type="text"
48
value={localValue}
49
style={inputStyle}
50
onChange={handleInputChange} />
51
<button style={buttonStyle} onClick={handleSaveClick}>Ok</button>
52
</div>
53
);
54
}
55
if (localMode === 'read') {
56
const handleEditClick = () => {
57
setLocalMode('edit');
58
};
59
return (
60
<div style={textStyle} onClick={handleEditClick}>{localValue}</div>
61
);
62
}
63
return null;
64
});
65
66
// Wiersz ------------------------------------------
67
68
const tdStyle = {
69
padding: '1px',
70
border: '1px solid black',
71
};
72
73
const optionStyle = {
74
tdStyle,
75
width: '30px'
76
};
77
78
const Row = React.memo(({ mode, columns, data, onChange, onDelete}) => {
79
const handleDeleteClick = () => onDelete?.();
80
return (
81
<tr>
82
{columns.map(({path}, columnIndex) => {
83
const handleChange = value => {
84
if (onChange) {
85
const changedData = { data, [path]: value };
86
onChange(columnIndex, changedData);
87
}
88
};
89
return (
90
<td key={path} style={tdStyle}>
91
<Cell
92
mode={mode}
93
value={data[path]}
94
onChange={handleChange}
95
/>
96
</td>
97
);
98
})}
99
<td style={optionStyle}>
100
<button onClick={handleDeleteClick}>Usuń</button>
101
</td>
102
</tr>
103
);
104
});
105
106
// TABELA ----------------------------------------
107
108
const tableStyle = {
109
border: '1px solid black',
110
borderCollapse: 'collapse',
111
width: '100%'
112
}
113
114
const Table = React.memo(({id, columns, data, onAdd, onChange, onDelete}) => {
115
const [addedIndex, setAddedIndex] = React.useState();
116
const handleAddClick = () => {
117
onAdd?.(data.length);
118
setAddedIndex(data.length);
119
};
120
return (
121
<div>
122
<table style={tableStyle}>
123
<tbody>
124
<tr>
125
{columns.map(({path, name}) => (
126
<th key={path} style={tdStyle}>{name}</th>
127
))}
128
</tr>
129
{data.map((rowData, rowIndex) => {
130
const handleChange = (columnIndex, changedData) => {
131
onChange?.(rowIndex, columnIndex, changedData);
132
};
133
const handleDelete = () => {
134
if (rowIndex !== addedIndex) {
135
setAddedIndex(addedIndex - 1);
136
}
137
onDelete?.(rowIndex, rowData);
138
};
139
return (
140
<Row
141
key={rowData[id]}
142
mode={addedIndex === rowIndex ? 'edit' : 'read'}
143
columns={columns}
144
data={rowData}
145
onChange={handleChange}
146
onDelete={handleDelete}
147
/>
148
);
149
})}
150
</tbody>
151
</table>
152
<br />
153
<div>
154
<button onClick={handleAddClick}>Dodaj wiersz</button>
155
</div>
156
</div>
157
);
158
});
159
160
// PRZYKŁAD --------------------------------------
161
162
const columns = [
163
{ path: 'id', name: 'ID' },
164
{ path: 'name', name: 'Imię' },
165
{ path: 'age', name: 'Wiek' }
166
];
167
168
let counter = 0;
169
170
const App = () => {
171
const [data, setData] = React.useState(() => ([
172
{ id: ++counter, name: 'Bartek', age: 22 },
173
{ id: ++counter, name: 'Adam', age: 43 },
174
{ id: ++counter, name: 'Marek', age: 16 },
175
{ id: ++counter, name: 'Jacek', age: 29 }
176
]));
177
const handleAdd = (rowIndex) => {
178
const newRowData = { id: ++counter };
179
setData([ data, newRowData ]);
180
//TODO: żądanie AJAX do serwera
181
console.log(`Dodano pusty wiersz!`);
182
};
183
const handleChange = (rowIndex, columnIndex, changedRowData) => {
184
const columnData = columns[columnIndex];
185
const changedRowJson = JSON.stringify(changedRowData, null, 4);
186
//TODO: żądanie AJAX do serwera
187
console.log(`Changed row:\n${changedRowJson}`);
188
};
189
const handleDelete = (rowIndex, removedRowData) => {
190
setData(data.filter((value, index) => index !== rowIndex));
191
//TODO: żądanie AJAX do serwera
192
console.log(`Usunięto wiersz: ${rowIndex}`);
193
};
194
return (
195
<div>
196
<Table
197
id="id"
198
columns={columns}
199
data={data}
200
onAdd={handleAdd}
201
onChange={handleChange}
202
onDelete={handleDelete}
203
/>
204
</div >
205
);
206
};
207
208
const root = document.querySelector('#root');
209
ReactDOM.render(<App/>, root );