Удаление / перемещение определенного элемента в массиве в React - PullRequest
2 голосов
/ 23 октября 2019

У меня следующая ситуация, я создал список, который добавляет (после нажатия кнопки) React Node. Окончательный макет модели выглядит примерно так:

enter image description here

Текущий код следующий:

import * as React from 'react';
import { Icon } from '../icon/icon';
import { Button } from '../..';

export interface PriorizationListProps {
    children: React.ReactNode;
    limit?: number;
}

export const PriorizationList: React.FunctionComponent<PriorizationListProps> = ({ limit, children }) => {
    const [items, setItems] = React.useState<React.ReactNode[]>([]);

    const move = (currentIndex: number, futureIndex: number) => {
        if (futureIndex !== -1 && futureIndex < items.length) {
            const item = items[currentIndex];
            const movingItem = items[futureIndex];
            items[currentIndex] = movingItem;
            items[futureIndex] = item;
            setItems(items);
        }
    };

    const deleteItem = (index: number) => {
        setItems(prevItems => prevItems.filter((_,i) => i !== index));
    };

    const addItem = () => {
        const newItems = items.concat([children]);
        setItems(newItems);
    };

    const disabledByLimit = () => limit === items.length;

    return (
        <>
            {items.map((item, index) => {
                return (
                    <div className="PriorizationList">
                        <span className="PriorizationList-order">{`${index + 1}.`}</span>
                        <span className="PriorizationList-content">{item}</span>
                        <span className="PriorizationList-icon">
                            <Icon type="pushUp" onClick={() => move(index, index - 1)} />
                            <Icon type="pushDown" onClick={() => move(index, index + 1)} />
                            <Icon type="delete" onClick={() => deleteItem(index)} />
                        </span>
                    </div>
                );
            })}
            <Button type="text" className="PriorizationList-button" onClick={addItem} disabled={disabledByLimit()}>
                <span>Add</span>
                <Icon type="add" />
            </Button>
        </>
    );
};

PriorizationList.defaultProps = {
    limit: 10
};

export default PriorizationList;

Два вопроса, которые я 'Когда я удаляю элемент из списка, он всегда удаляет последний. И движущиеся стрелки не работают. Я пробовал несколько изменений, но ни одно из них не сработало.

1 Ответ

1 голос
/ 23 октября 2019

Я считаю, что items.concat([children]) создает новые компоненты, поэтому вам также нужно будет передавать методы move() и delete() в качестве обратных вызовов (или передавать элементы в качестве аргумента). Кроме того, items.concat([children]) копирует старые компоненты (включая реквизиты в их начальных значениях) и переводит их в состояние, таким образом, только последний элемент получает конечное состояние (фактически его вероятный позади, то есть ваш первый элемент). рендерингу передается пустой массив this.state.items, поскольку он еще не включит себя, так как состояние устанавливается после создания).

Возможно, попробуйте либо преобразовать в компонент на основе классов, который позволит вам явно указывать привязку this, либо использовать метод типа renderItems для создания содержимого без создания новых компонентов (показано в моем фрагменте ниже)

Также:

  • Избегайте мутирования массива состояний с [... items], где это необходимо.
  • Требуется динамически генерируемый контент (например, с циклом или картой)иметь уникальную ключевую опору для каждого сгенерированного предмета. Здесь я использовал Math.random() (который может не подходить для производства)
  • Вы должны проверить futureIndex < items.length

Я создал рабочий упрощенный / переработанный пример в JSX, которыйможет помочь вам добраться туда, куда вам нужно идти

ОБНОВЛЕННЫЙ фрагмент кода с использованием renderItems

const PriorizationList = () => {
    const [items, setItems] = React.useState([]);
    
    const move = (currentIndex, futureIndex) => {
        if (futureIndex !== -1 && futureIndex < items.length ) {
            const tempItemsAray = [...items]
            const item = tempItemsAray[currentIndex];
            const movingItem = tempItemsAray[futureIndex];
            tempItemsAray[currentIndex] = movingItem;
            tempItemsAray[futureIndex] = item;
            setItems(tempItemsAray);
        }
    };
    const deleteItem = (index) => {
        setItems(prevItems => prevItems.filter((_,i) => i !== index));
    };
    const addItem = () => {
        const id = String(Math.floor(Math.random() * 100000))
        const newItems = [...items , { id }];
        setItems(newItems);
    };

    const renderItems = () => {
        return items.map((item, index) => {
            return (
                <div>
                    <div>
                        <span>Item: {item.id}</span>
                        <button type="pushUp" onClick={() => move(index, index - 1)} >up</button>
                        <button type="pushDown" onClick={() => move(index, index + 1)} >down</button>
                        <button type="delete" onClick={() => deleteItem(index)} >delete</button>
                    </div>
                </div>
            );
        }) 
    }

    return (
        <div>
            <button type="text" onClick={addItem} > Add </button>
            
            {renderItems()}
        </div>
    );
};

ReactDOM.render(
    <PriorizationList />,
    document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="react"></div>

Предыдущий фрагмент передачи с обратными вызовами

const PriorizationList = () => {
    const [items, setItems] = React.useState([]);
    
    const move = (currentIndex, futureIndex) => {
        if (futureIndex !== -1 && futureIndex < items.length ) {
            const tempItemsAray = [...items]
            const item = tempItemsAray[currentIndex];
            const movingItem = tempItemsAray[futureIndex];
            tempItemsAray[currentIndex] = movingItem;
            tempItemsAray[futureIndex] = item;
            setItems(tempItemsAray);
        }
    };
    const deleteItem = (index) => {
        setItems(prevItems => prevItems.filter((_,i) => i !== index));
    };
    const addItem = () => {
        const id = String(Math.floor(Math.random() * 100000))
        const newItems = [...items , { id , jsx : LineItemJSX }];
        setItems(newItems);
    };
    
    const LineItemJSX = ({deleteItem , move , index , item}) => {
        return (
            <div>
                <span>Item: {item.id}</span>
                <button type="pushUp" onClick={() => move(index, index - 1)} >up</button>
                <button type="pushDown" onClick={() => move(index, index + 1)} >down</button>
                <button type="delete" onClick={() => deleteItem(index)} >delete</button>
            </div>
        )
    }
    return (
        <div>
            <button type="text" onClick={addItem} > Add </button>

            {items.map((item, index) => {
                const Component = item.jsx
                return (
                    <Component index={index} key={item.id} deleteItem={deleteItem} move={move} item={item}/>
                );
            })}
        </div>
    );
};

ReactDOM.render(
    <PriorizationList />,
    document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="react"></div>
Предыдущий фрагмент, проходящий с элементами, указывается в виде проп

const PriorizationList = () => {
    const [items, setItems] = React.useState([]);
    
    const move = (items , currentIndex, futureIndex) => {
        if (futureIndex !== -1 && futureIndex < items.length ) {
            const tempItemsAray = [...items]
            const item = tempItemsAray[currentIndex];
            const movingItem = tempItemsAray[futureIndex];
            tempItemsAray[currentIndex] = movingItem;
            tempItemsAray[futureIndex] = item;
            setItems(tempItemsAray);
        }
    };
    const deleteItem = (index) => {
        setItems(prevItems => prevItems.filter((_,i) => i !== index));
    };
    const addItem = () => {
        const id = String(Math.floor(Math.random() * 100000))
        const newItems = [...items , { id , jsx : LineItemJSX }];
        setItems(newItems);
    };
    
    const LineItemJSX = ({items , index , item}) => {
        return (
            <div>
                <span>Item: {item.id}</span>
                <button type="pushUp" onClick={() => move(items ,index, index - 1)} >up</button>
                <button type="pushDown" onClick={() => move(items ,index, index + 1)} >down</button>
                <button type="delete" onClick={() => deleteItem(index)} >delete</button>
            </div>
        )
    }
    return (
        <div>
            <button type="text" onClick={addItem} > Add </button>

            {items.map((item, index) => {
                const Component = item.jsx
                return (
                    <Component index={index} key={item.id} item={item} items={items}/>
                );
            })}
        </div>
    );
};

ReactDOM.render(
    <PriorizationList />,
    document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="react"></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...