2

So I have some data I would like to cycle through:

const data = {
  names: [
    {
      name: "Jordan"
      // additional data
    },
    {
      name: "Holly"
      // additional data
    },
    {
      name: "Sean"
      // additional data
    }
  ]
};

Using useState hook, I start my data from the index of 0:

const [index, setIndex] = useState(data.names[0]);

How can I cycle through the objects in my array using state? I have created a button that calls handleClick, how can I +1 through the array?

function App() {
  const [index, setIndex] = useState(data.names[0]);

  function handleClick() {
    setIndex(); // Help!
  }

  return (
    <div className="App">
      <h1>{index.name}</h1>
      <button onClick={handleClick}>Change name</button>
    </div>
  );
}

CodeSandBox

Jordan
  • 1,101
  • 1
  • 12
  • 26

1 Answers1

6

Don't set the element from the array in the state. Instead use the current index. In the handler add 1 to the index on each click, and to circle the names get the remainder from the length. Whenever the index is equal to the array's length, the remainder would be 0.

Note: the mod() function is needed for backwards cycling, since the remainder operator (%) doesn't work well with negative numbers. It was taken from this answer.

const { useState, useCallback } = React;

const mod = (n, m) => ((n % m) + m) % m;

function App({ names }) {
  const [index, setIndex] = useState(0);

  const forwards = useCallback(() => 
    setIndex(state => mod(state + 1, names.length))
  , [setIndex, names]);
  
  const backwards = useCallback(() => 
    setIndex(state => mod(state - 1, names.length))
  , [setIndex, names]);

  return (
    <div className="App">
      <h1>{names[index].name}</h1>
      <button onClick={backwards}>Backwards</button>
      <button onClick={forwards}>Forwards</button>
    </div>
  );
}

const data = {"names":[{"name":"Jordan"},{"name":"Holly"},{"name":"Sean"}]};

const rootElement = document.getElementById("root");
ReactDOM.render(<App {...data} />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • That's great! Especially with the looping - If I added a second button to cycle backwards, how could I do that? – Jordan Dec 23 '19 at 18:19
  • Ideally you would create another function, and use -1. However, JS % operator doesn't do well with negative numbers. I've added a `mod()` function, taken from this [answer](https://stackoverflow.com/questions/4467539/javascript-modulo-gives-a-negative-result-for-negative-numbers), that gets the remainder from negative numbers as well. – Ori Drori Dec 23 '19 at 18:29