[英]Best way to implement dynamic array of inputs in React
目前我已经使用普通的 javascript 数组实现了它。 但似乎问题很少。
onChange
, onDelete
必须遍历整个数组,这可能更好像 O(1) 使用immutable.js
可以摆脱问题 1。数字 2 真的是问题吗? 有比immutable.js
更好的选择吗? 也许我没有问正确的问题,最好的方法是什么?
import React, { useState } from "react";
export default function() {
const [rows, setRows] = useState([""]);
const onChange = (e, i) => {
setRows(rows.map((row, index) => (index !== i ? row : e.target.value)));
};
const onDelete = i => {
setRows(rows.filter((_, index) => i !== index));
};
return (
<>
{rows.map((row, index) => {
return (
<div key={index}>
<input value={row} onChange={e => onChange(e, index)} />
{index !== 0 && (
<button onClick={() => onDelete(index)}>
- delete row
</button>
)}
</div>
);
})}
<button onClick={() => setRows(rows.concat([""]))}>
+ add row
</button>
</>
);
}
更新
我尝试使用immutable-js
OrderedMap
。 现在元素的key
属性不会改变并且onChange
和onDelete
不会遍历所有内容。 这比以前好吗?
import React, { useState } from "react";
import { OrderedMap } from "immutable";
export default function() {
const [inputState, setInputState] = useState({
rows: OrderedMap(),
nextKey: Number.MIN_VALUE,
});
function onChange(k, v) {
setInputState({
...inputState,
rows: inputState.rows.update(k, () => v),
});
}
function addRow() {
const { rows, nextKey } = inputState;
setInputState({
rows: rows.set(nextKey, ""),
nextKey: nextKey + 1,
});
}
function deleteItem(k) {
setInputState({
...inputState,
rows: inputState.rows.delete(k),
});
}
return (
<>
{inputState.rows.entrySeq().map(([k, v]) => {
return (
<div key={k}>
<input
value={v}
onChange={({ target: { value } }) => {
onChange(k, value);
}}
/>
<button onClick={() => deleteItem(k)}>-</button>
</div>
);
})}
<button onClick={addRow}>+ add row</button>
</>
);
}
更新:2也尝试使用普通的 javascript Map
。
import React, { useState } from "react";
export default function() {
const [inputState, setInputState] = useState({
rows: new Map(),
nextKey: Number.MIN_VALUE,
});
function onChange(k, v) {
const { rows, nextKey } = inputState;
rows.set(k, v);
setInputState({
nextKey,
rows,
});
}
function addRow() {
const { rows, nextKey } = inputState;
rows.set(nextKey, "");
setInputState({
rows,
nextKey: nextKey + 1,
});
}
function deleteItem(k) {
const { rows, nextKey } = inputState;
rows.delete(k);
setInputState({
nextKey,
rows,
});
}
const uiList = [];
for (const [k, v] of inputState.rows.entries()) {
uiList.push(
<div key={k}>
<input
value={v}
onChange={({ target: { value } }) => {
onChange(k, value);
}}
/>
<button onClick={() => deleteItem(k)}>-</button>
</div>
);
}
return (
<>
{uiList}
<button onClick={addRow}>+ add row</button>
</>
);
}
更新 3使用普通数组但具有关键属性。
import React, { useState } from "react";
export default function() {
const [inputState, setInputState] = useState({
rows: [],
nextKey: Number.MIN_VALUE,
});
function onChange(i, v) {
const { rows, nextKey } = inputState;
rows[i].value = v;
setInputState({
nextKey,
rows,
});
}
function addRow() {
const { rows, nextKey } = inputState;
rows.push({ key: nextKey, value: "" });
setInputState({
rows,
nextKey: nextKey + 1,
});
}
function deleteItem(i) {
const { rows, nextKey } = inputState;
rows.splice(i, 1);
setInputState({
nextKey,
rows,
});
}
return (
<>
{inputState.rows.map(({ key, value }, index) => {
return (
<div key={key}>
<input
value={value}
onChange={({ target: { value } }) => {
onChange(index, value);
}}
/>
<button onClick={() => deleteItem(index)}>-</button>
</div>
);
})}
<button onClick={addRow}>+ add row</button>
</>
);
}
不使用外部js,我只是对代码做一点点修改,不用做map或者filter,直接用splice就可以了,
下面的代码将有助于快速删除,并且在删除后也会保持相同的索引。
import React, { useState } from "react";
export default function() {
const [rows, setRows] = useState([""]);
const onChange = (e, i) => {
const {value} = e.target;
setRows(prev => {
prev[i] = value;
return [...prev];
});
};
const onDelete = i => {
setRows(prev => {
prev.splice(i,1, undefined);
return [...prev];
});
};
return (
<>
{rows.map((row, index) => (typeof row !== "undefined") && (
<div key={index}>
<input value={row} onChange={e => onChange(e, index)} />
{index !== 0 && (
<button onClick={() => onDelete(index)}>
- delete row
</button>
)}
</div>
))}
<button onClick={() => setRows(rows.concat([""]))}>
+ add row
</button>
</>
);
}
到目前为止,最好的选择是
immutable
库完全符合第 2 点和第 3 点。 由于它可以有效地更新和删除而无需更改,因此它似乎是一个不错的选择。
import React, { useState, useEffect } from "react";
import { OrderedMap } from "immutable";
function UsingImmutableJS({ onChange }) {
const [inputState, setInputState] = useState({
rows: OrderedMap(),
nextKey: 0,
});
useEffect(() => {
if (onChange) onChange([...inputState.rows.values()]);
}, [inputState, onChange]);
function onInputChange(k, v) {
setInputState(prev => ({
...prev,
rows: prev.rows.update(k, () => v),
}));
}
function addRow() {
setInputState(prev => ({
nextKey: prev.nextKey + 1,
rows: prev.rows.set(prev.nextKey, ""),
}));
}
function deleteItem(k) {
setInputState(prev => ({
...prev,
rows: prev.rows.delete(k),
}));
}
return (
<>
{inputState.rows.entrySeq().map(([k, v]) => {
return (
<div key={k}>
<input
value={v}
onChange={({ target: { value } }) => {
onInputChange(k, value);
}}
/>
<button onClick={() => deleteItem(k)}>-</button>
</div>
);
})}
<button onClick={addRow}>+ add row</button>
<button onClick={() => console.log([...inputState.rows.values()])}>
print
</button>
</>
);
}
没有索引我们可以使用 Map 和 Map 检索更快,您可以尝试下面的代码。
import React, { useState } from "react";
export default function() {
const [rows, setRows] = useState(new Map());
const onUpdate = (key, value) => {
setRows(prev => new Map(prev).set(key, { key, value}));
}
const onDelete = (key) => {
setRows(prev => new Map(prev).delete(key));
}
return (
<>
{[...rows].map((row, index) => (typeof row[1].value !== "undefined") && (
<div key={index}>
<input value={row[1].value} onChange={e => onUpdate(row[1].key, e.target.value)} />
<button onClick={() => onDelete(row[1].key)}>- delete row</button>
</div>
))}
<button onClick={() => onUpdate(rows.size, "")}>
+ add row
</button>
</>
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.