Home > Software engineering >  Implementing a function to swap array elements in React without mutating the original array
Implementing a function to swap array elements in React without mutating the original array

Time:09-21

I am coding a react app in which a user can click a button to swap an item in an array with the item to its left. I wrote a function to implement this without mutating the original items array that is rendering on the page, but this function is not doing anything to my code, nor is it returning any errors.

Here is my app component, which defines the function swapLeft then passes that function down to the Item component as props:

import React, { useState } from "react";
import Form from "./components/Form";
import Item from "./components/Item";
import { nanoid } from "nanoid";
import './App.css';

function App(props) {
  const [items, setItems] = useState(props.items);

  function deleteItem(id) {
    const remainingItems = items.filter(item => id !== item.id);
    setItems(remainingItems);
  }

  function swapLeft(index) {
    const index2 = index - 1;
    const newItems = items.slice();
    newItems[index] = items[index2];
    newItems[index2] = items[index];
    return newItems;
  }

  const itemList = items
  .map((item, index) => (
    <Item
      id={item.id}
      index={index}
      name={item.name}
      key={item.id}
      deleteItem={deleteItem}
      swapLeft={swapLeft}
    />
  ));


  function addItem(name) {
    const newItem = { id: "item-"   nanoid(), name: name };
    setItems([...items, newItem]);
  }


  return (
    <div className="form">
      <Form addItem={addItem} />
      <ul className="names">
        {itemList}
      </ul>
    </div>
  );
}

export default App;

And the Item component:

import React from "react";
import {  Button, Card, CardContent, CardHeader } from 'semantic-ui-react'

export default function Item(props) {
    return (
        
      <Card>
        <CardContent>
         <CardHeader> {props.name}</CardHeader>
          <Button onClick={() => props.deleteItem(props.id)}>
            Delete <span className="visually-hidden"> {props.name}</span>
          </Button>
          </CardContent>
          <CardContent style={{ display: 'flex' }}>
             <i className="arrow left icon" onClick={() => props.swapLeft(props.index)} style={{ color: 'blue'}}></i>
            <i className="arrow right icon"  style={{ color: 'blue'}}></i>
           </CardContent>
        </Card>
        
    );
  }

Is there a better way for me to write this function and implement this? I suppose I could do something with the React setState hook, but this seemed like an easier solution. I am new to React so any insight would be helpful

CodePudding user response:

The way React knows if the state has changed is whether the state is refers to an entirely different address in memory. In case of arrays, if you want React to rerender the page because the array in the state changed, you need to provide it an entirely new array. Modifying the existing array will not trigger the render process.

Basically, what you need to do is changed the last line of swapLeft function to

setItems(newItems)

If you want the changes to take effect immediately (which is what I guess you want to do here)

You can also use the return value from the function and change the state in another component, FYI.

EDIT:

I looked at this again, and your implementation of swap is also wrong, but even if you corrected it you still wouldn't see a change, unless you did what I mentioned above

The full correct function would be

 function swapLeft(index) {
    const index2 = index - 1;
    const newItems = items.slice();
    const temp = items[index];
    newItems[index] = items[index2];
    newItems[index2] = temp;
    setItems(newItems);
  }

CodePudding user response:

Just to maybe clarify the previous one. If you don't call setState, your component doesn't rerender. This means that no matter what you do with those arrays, it won't be visible on the screen.

  • Related