wkkkis-hooks

use-array-state (use-array-state)

Manages an array as a React state with built-in array manipulation methods.

v0.1.0

Usage

import { use-array-state } from "@/hooks"
1"use client"; 2 3import React, { useState } from "react"; 4 5import { Badge } from "@/components/ui/badge"; 6import { Button } from "@/components/ui/button"; 7import { 8 Card, 9 CardContent, 10 CardDescription, 11 CardFooter, 12 CardHeader, 13 CardTitle, 14} from "@/components/ui/card"; 15import { Input } from "@/components/ui/input"; 16 17import { useArrayState } from "@/components/hooks/use-array-state"; 18 19export function UseArrayStateDemo() { 20 const [inputValue, setInputValue] = useState(""); 21 const [updateIndex, setUpdateIndex] = useState(""); 22 const [updateValue, setUpdateValue] = useState(""); 23 24 const { 25 array, 26 length, 27 isEmpty, 28 first, 29 last, 30 push, 31 pop, 32 shift, 33 unshift, 34 insert, 35 remove, 36 update, 37 clear, 38 reset, 39 sort, 40 reverse, 41 filter, 42 } = useArrayState<string>({ 43 initialValue: ["React", "TypeScript", "Next.js"], 44 onChange: (newArray) => { 45 console.log("Array changed:", newArray); 46 }, 47 }); 48 49 const handleAddItem = () => { 50 if (inputValue.trim()) { 51 push(inputValue.trim()); 52 setInputValue(""); 53 } 54 }; 55 56 const handleUpdateItem = () => { 57 const index = parseInt(updateIndex); 58 if (!isNaN(index) && updateValue.trim()) { 59 update(index, updateValue.trim()); 60 setUpdateIndex(""); 61 setUpdateValue(""); 62 } 63 }; 64 65 const handleInsertAtStart = () => { 66 if (inputValue.trim()) { 67 unshift(inputValue.trim()); 68 setInputValue(""); 69 } 70 }; 71 72 const handleRemoveItem = (index: number) => { 73 remove(index); 74 }; 75 76 const handleFilterLongItems = () => { 77 filter((item) => item.length <= 6); 78 }; 79 80 return ( 81 <Card className="w-full max-w-2xl mx-auto"> 82 <CardHeader> 83 <CardTitle>useArrayState</CardTitle> 84 <CardDescription> 85 A powerful hook for managing arrays as React state with built-in 86 manipulation methods. 87 </CardDescription> 88 </CardHeader> 89 <CardContent className="space-y-6"> 90 {/* Array Display */} 91 <div> 92 <h3 className="text-sm font-medium mb-2">Current Array:</h3> 93 <div className="flex flex-wrap gap-2 min-h-[40px] p-3 border rounded-md bg-muted/20"> 94 {array.map((item, index) => ( 95 <Badge 96 key={index} 97 variant="secondary" 98 className="cursor-pointer hover:bg-destructive hover:text-destructive-foreground" 99 onClick={() => handleRemoveItem(index)} 100 > 101 {item} × 102 </Badge> 103 ))} 104 {isEmpty && ( 105 <span className="text-muted-foreground text-sm"> 106 Array is empty 107 </span> 108 )} 109 </div> 110 </div> 111 112 {/* Array Stats */} 113 <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> 114 <div className="text-center"> 115 <div className="text-2xl font-bold">{length}</div> 116 <div className="text-xs text-muted-foreground">Length</div> 117 </div> 118 <div className="text-center"> 119 <div className="text-sm font-medium truncate">{first || "N/A"}</div> 120 <div className="text-xs text-muted-foreground">First</div> 121 </div> 122 <div className="text-center"> 123 <div className="text-sm font-medium truncate">{last || "N/A"}</div> 124 <div className="text-xs text-muted-foreground">Last</div> 125 </div> 126 <div className="text-center"> 127 <div className="text-sm font-medium">{isEmpty ? "Yes" : "No"}</div> 128 <div className="text-xs text-muted-foreground">Empty</div> 129 </div> 130 </div> 131 132 <div className="border-t" /> 133 134 {/* Add Items */} 135 <div className="space-y-3"> 136 <h3 className="text-sm font-medium">Add Items:</h3> 137 <div className="flex gap-2"> 138 <Input 139 placeholder="Enter item to add" 140 value={inputValue} 141 onChange={(e) => setInputValue(e.target.value)} 142 onKeyDown={(e) => { 143 if (e.key === "Enter") { 144 handleAddItem(); 145 } 146 }} 147 /> 148 <Button onClick={handleAddItem} disabled={!inputValue.trim()}> 149 Add to End 150 </Button> 151 <Button 152 onClick={handleInsertAtStart} 153 disabled={!inputValue.trim()} 154 variant="outline" 155 > 156 Add to Start 157 </Button> 158 </div> 159 </div> 160 161 {/* Update Item */} 162 <div className="space-y-3"> 163 <h3 className="text-sm font-medium">Update Item:</h3> 164 <div className="flex gap-2"> 165 <Input 166 placeholder="Index" 167 value={updateIndex} 168 onChange={(e) => setUpdateIndex(e.target.value)} 169 className="w-20" 170 /> 171 <Input 172 placeholder="New value" 173 value={updateValue} 174 onChange={(e) => setUpdateValue(e.target.value)} 175 onKeyDown={(e) => { 176 if (e.key === "Enter") { 177 handleUpdateItem(); 178 } 179 }} 180 /> 181 <Button 182 onClick={handleUpdateItem} 183 disabled={!updateIndex.trim() || !updateValue.trim()} 184 variant="outline" 185 > 186 Update 187 </Button> 188 </div> 189 </div> 190 191 <div className="border-t" /> 192 193 {/* Array Operations */} 194 <div className="space-y-3"> 195 <h3 className="text-sm font-medium">Array Operations:</h3> 196 <div className="grid grid-cols-2 md:grid-cols-4 gap-2"> 197 <Button 198 onClick={() => pop()} 199 disabled={isEmpty} 200 variant="outline" 201 size="sm" 202 > 203 Remove Last 204 </Button> 205 <Button 206 onClick={() => shift()} 207 disabled={isEmpty} 208 variant="outline" 209 size="sm" 210 > 211 Remove First 212 </Button> 213 <Button 214 onClick={() => sort()} 215 disabled={isEmpty} 216 variant="outline" 217 size="sm" 218 > 219 Sort A-Z 220 </Button> 221 <Button 222 onClick={() => reverse()} 223 disabled={isEmpty} 224 variant="outline" 225 size="sm" 226 > 227 Reverse 228 </Button> 229 <Button 230 onClick={handleFilterLongItems} 231 disabled={isEmpty} 232 variant="outline" 233 size="sm" 234 > 235 Filter Short 236 </Button> 237 <Button 238 onClick={clear} 239 disabled={isEmpty} 240 variant="destructive" 241 size="sm" 242 > 243 Clear All 244 </Button> 245 <Button onClick={reset} variant="outline" size="sm"> 246 Reset 247 </Button> 248 </div> 249 </div> 250 </CardContent> 251 <CardFooter className="text-xs text-muted-foreground"> 252 Click on items to remove them. Use the operations above to manipulate 253 the array. 254 </CardFooter> 255 </Card> 256 ); 257} 258

Install

npx wkkkis-hooks add use-array-state

Options

NameTypeDefaultDescription
UseArrayStateOptions{}Configuration options for the hook
initialValueT[][]The initial array value
onChange(array: T[]) => voidnullCallback called whenever the array changes

Type Safety

  • The hook is fully type-safe with TypeScript generics.
  • All methods maintain type information throughout operations.
  • Prevents runtime errors with proper type checking.

Performance Optimization

  • Uses useCallback to memoize all methods for optimal performance.
  • Prevents unnecessary re-renders by checking for actual changes.
  • Efficiently handles large arrays with immutable updates.

Change Detection

  • The onChange callback is called whenever the array state changes.
  • Changes are detected at the reference level for performance.
  • Callbacks receive the new array as their argument.

SSR Compatibility

  • Fully compatible with Server-Side Rendering.
  • No browser-specific APIs used.
  • Safe for use in Next.js and other SSR frameworks.

Files in this hook

SourceDestination
files/use-array-state.tssrc/hooks/use-array-state.ts