CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-sortable-hoc

Higher-order components to turn any list into animated, accessible and touch-friendly sortable lists

Pending
Overview
Eval results
Files

array-utilities.mddocs/

Array Utilities

Utility functions for reordering arrays after sort operations. Note that arrayMove is deprecated in react-sortable-hoc v2.0.0 and users should install the separate array-move package instead.

Capabilities

arrayMove Function (Deprecated)

Reorders array elements by moving an item from one index to another.

/**
 * Utility function to reorder array items (DEPRECATED)
 * @param array - Array to reorder  
 * @param from - Source index
 * @param to - Target index
 * @returns New array with moved item
 * @deprecated Use the 'array-move' package instead
 */
function arrayMove<T>(
  array: T[],
  from: number,
  to: number
): T[];

⚠️ Deprecation Warning: This function will be removed in the next major release. Install the array-move package separately:

npm install array-move

Usage Examples:

import { arrayMove } from 'react-sortable-hoc'; // Deprecated
// OR (recommended)
import { arrayMove } from 'array-move';

// Basic array reordering
const items = ['A', 'B', 'C', 'D', 'E'];
const reordered = arrayMove(items, 1, 3); // Move 'B' to position 3
// Result: ['A', 'C', 'D', 'B', 'E']

// Move to beginning
const toStart = arrayMove(items, 3, 0); // Move 'D' to start
// Result: ['D', 'A', 'B', 'C', 'E']

// Move to end
const toEnd = arrayMove(items, 0, -1); // Move 'A' to end
// Result: ['B', 'C', 'D', 'E', 'A']

Integration with SortableContainer

The primary use case is handling the onSortEnd callback:

import React, { useState } from 'react';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
import { arrayMove } from 'array-move'; // Recommended

const SortableItem = SortableElement(({ value }) => <li>{value}</li>);

const SortableList = SortableContainer(({ items }) => (
  <ul>
    {items.map((value, index) => (
      <SortableItem key={`item-${index}`} index={index} value={value} />
    ))}
  </ul>
));

const App = () => {
  const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);

  const onSortEnd = ({ oldIndex, newIndex }) => {
    setItems(arrayMove(items, oldIndex, newIndex));
  };

  return <SortableList items={items} onSortEnd={onSortEnd} />;
};

Advanced Array Operations

Moving Multiple Items

For moving multiple selected items:

const moveMultipleItems = (array: any[], selectedIndices: number[], targetIndex: number) => {
  const selectedItems = selectedIndices.map(index => array[index]);
  const remainingItems = array.filter((_, index) => !selectedIndices.includes(index));
  
  // Insert selected items at target position
  const result = [...remainingItems];
  result.splice(targetIndex, 0, ...selectedItems);
  
  return result;
};

// Usage
const items = ['A', 'B', 'C', 'D', 'E'];
const moved = moveMultipleItems(items, [1, 3], 0); // Move 'B' and 'D' to start
// Result: ['B', 'D', 'A', 'C', 'E']

Conditional Array Moves

Only move items when conditions are met:

const conditionalMove = (
  array: any[], 
  oldIndex: number, 
  newIndex: number, 
  condition: (item: any) => boolean
) => {
  const item = array[oldIndex];
  
  if (!condition(item)) {
    return array; // No change if condition fails
  }
  
  return arrayMove(array, oldIndex, newIndex);
};

// Usage
const onSortEnd = ({ oldIndex, newIndex }) => {
  setItems(prevItems => 
    conditionalMove(
      prevItems, 
      oldIndex, 
      newIndex, 
      item => !item.locked // Only move unlocked items
    )
  );
};

Array Move with Collections

When working with multiple collections:

const moveWithinCollection = (
  items: any[], 
  oldIndex: number, 
  newIndex: number, 
  collection: string
) => {
  // Filter items by collection
  const collectionItems = items.filter(item => item.collection === collection);
  const otherItems = items.filter(item => item.collection !== collection);
  
  // Move within collection
  const reorderedCollection = arrayMove(collectionItems, oldIndex, newIndex);
  
  // Merge back
  return [...otherItems, ...reorderedCollection];
};

const onSortEnd = ({ oldIndex, newIndex, collection }) => {
  setItems(prevItems => 
    moveWithinCollection(prevItems, oldIndex, newIndex, collection)
  );
};

Array Move Alternatives

Using Array.splice()

Manual implementation without external dependency:

const manualArrayMove = <T>(array: T[], fromIndex: number, toIndex: number): T[] => {
  const result = array.slice(); // Create copy
  const [removed] = result.splice(fromIndex, 1); // Remove item
  result.splice(toIndex, 0, removed); // Insert at new position
  return result;
};

Using Immutable Updates

For immutable state management:

import { produce } from 'immer';

const immerArrayMove = produce((draft, fromIndex, toIndex) => {
  const [removed] = draft.splice(fromIndex, 1);
  draft.splice(toIndex, 0, removed);
});

// Usage
const onSortEnd = ({ oldIndex, newIndex }) => {
  setItems(prevItems => immerArrayMove(prevItems, oldIndex, newIndex));
};

Performance Considerations

Large Arrays

For large arrays, consider using keys based on item IDs rather than indices:

const SortableItem = SortableElement(({ item }) => <li>{item.name}</li>);

const SortableList = SortableContainer(({ items }) => (
  <ul>
    {items.map((item, index) => (
      <SortableItem 
        key={item.id} // Use stable ID, not index
        index={index} 
        item={item} 
      />
    ))}
  </ul>
));

Memoization

Memoize expensive array operations:

import { useMemo } from 'react';

const App = () => {
  const [items, setItems] = useState(largeItemList);

  const sortedItems = useMemo(() => {
    return items.sort((a, b) => a.priority - b.priority);
  }, [items]);

  const onSortEnd = ({ oldIndex, newIndex }) => {
    setItems(prevItems => arrayMove(prevItems, oldIndex, newIndex));
  };

  return <SortableList items={sortedItems} onSortEnd={onSortEnd} />;
};

Migration Guide

From react-sortable-hoc arrayMove

// Old (deprecated)
import { arrayMove } from 'react-sortable-hoc';

// New (recommended)
import { arrayMove } from 'array-move';

// The API is identical, just change the import
const reordered = arrayMove(items, oldIndex, newIndex);

Installation

# Install the separate package
npm install array-move

# TypeScript types are included
# No need for @types/array-move

Additional Features

The standalone array-move package provides additional utilities:

import { arrayMove, arrayMoveImmutable, arrayMoveMutable } from 'array-move';

// Immutable (returns new array)
const newArray = arrayMoveImmutable([1, 2, 3], 0, 2);

// Mutable (modifies original array)
arrayMoveMutable(originalArray, 0, 2);

// Standard (same as arrayMoveImmutable)
const moved = arrayMove([1, 2, 3], 0, 2);

Install with Tessl CLI

npx tessl i tessl/npm-react-sortable-hoc

docs

array-utilities.md

index.md

sortable-container.md

sortable-element.md

sortable-handle.md

tile.json