繁体   English   中英

在 React 中实现动态输入数组的最佳方法

[英]Best way to implement dynamic array of inputs in React

目前我已经使用普通的 javascript 数组实现了它。 但似乎问题很少。

  1. onChangeonDelete必须遍历整个数组,这可能更好像 O(1)
  2. 当某些项目被删除时,其他项目的关键属性会发生变化。 我不确定它是否坏。

使用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属性不会改变并且onChangeonDelete不会遍历所有内容。 这比以前好吗?

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>
        </>
    );
}

到目前为止,最好的选择是

  1. 生成密钥以用作密钥。
  2. 使用 map 进行高效的更新和删除。
  3. 不要改变状态。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM