Cycle Through List Items Mapped From a State (javascript)

Viewed 65

I have a list of items with nested data that are saved in the state of a component. I'm simply trying to map the item's information to the screen all while allowing the user to cycle through them with a next and previous button.

My issue: When incrementing or decrementing through the list of items, the app errors out.

How do I change the current object to the next or previous item?

Here's a working sample: https://codesandbox.io/s/peaceful-albattani-g3r6u?file=/src/App.js

Or here's the entire app.js:

import React, { useState } from "react";
import "./styles.css";
import {
  ButtonDropdown,
  DropdownToggle,
  DropdownMenu,
  DropdownItem,
  Button
} from "reactstrap";
import "bootstrap/dist/css/bootstrap.min.css";

export default function App() {
  const [dropdownOpen, setOpen] = useState(false);
  const toggle = () => setOpen(!dropdownOpen);
  const [currentIndex, setCurrentIndex] = useState({
    itemIndex: 0,
    locationIndex: 0
  });
  const [items, setItems] = useState([
    {
      item: 123,
      locations: [
        { location: "loc1", count: 100 },
        { location: "loc2", count: 200 }
      ]
    },
    {
      item: 456,
      locations: [
        { location: "loc3", count: 200 },
        { location: "loc4", count: 300 }
      ]
    },
    {
      item: 789,
      locations: [
        { location: "loc5", count: 100 },
        { location: "loc6", count: 600 }
      ]
    }
  ]);

  const deleteItem = (item) => {};

  const ddItemStyle = {
    display: "flex",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center"
  };

  return (
    <div className="App">
      <ButtonDropdown isOpen={dropdownOpen} toggle={toggle}>
        <DropdownToggle caret>locations</DropdownToggle>
        <DropdownMenu>
          {items[currentIndex.itemIndex].locations.map((location) => {
            return (
              <DropdownItem style={ddItemStyle}>
                <Button
                  onClick={() => {
                    deleteItem(location.location);
                  }}
                >
                  Delete
                </Button>
                : {location.location}{" "}
              </DropdownItem>
            );
          })}
        </DropdownMenu>
      </ButtonDropdown>
      <div style={{ height: "50%", width: "50%" }}>
        Item: {items[currentIndex.itemIndex].item}
      </div>
      <Button
        onClick={() => {
          setCurrentIndex({
            itemIndex: currentIndex.itemIndex + 1,
            locationIndex: 0
          });
        }}
      >
        Prev
      </Button>
      <Button
        onClick={() => {
          setCurrentIndex({
            itemIndex: currentIndex.itemIndex - 1,
            locationIndex: 0
          });
        }}
      >
        Next
      </Button>
    </div>
  );
}

2 Answers

First, your prev & next buttons need to be swapped around.

Also, you need to guard against going out of bounds; which is why you're getting that error:

<Button
    onClick={() => {
        // out of bounds check
        if(currentIndex.itemIndex <= 0) return;
        setCurrentIndex({
        itemIndex: currentIndex.itemIndex - 1,// decrement
        locationIndex: 0
        });
    }}
>
    Prev
</Button>
<Button
    onClick={() => {
        // out of bounds check
        if(currentIndex.itemIndex >= items.length - 1) return;
        setCurrentIndex({
        itemIndex: currentIndex.itemIndex + 1,// increment
        locationIndex: 0
        });
    }}
>
    Next
</Button>

Here is a live demo: https://codesandbox.io/s/crimson-cherry-s641v Also it will take care of the corner cases when the item is last and you try to press the next button or the item is first and you try to press the prev button.

Related