charon1212
@charon1212

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

【React】周期的な配列を管理したい

Q&A

Closed

解決したいこと

Reactで一定間隔ごとにAPIでデータを取得し、配列に追加することを考えています。単純に追加してしまうとデータがどんどん増えてしまうので、10件等の上限を決め、上限を超えたら1件目を上書きするような実装にしたいと考えてます。

  • イメージ:
['data1', 'data2', ] // 1回目  ※API問い合わせ1回で、複数のデータが飛んでくる。
['data1', 'data2', 'data3', ] // 2回目
['data1', 'data2', 'data3', ] // 3回目  ※API問い合わせで、データ0件もあり得る。
['data1', 'data2', 'data3', 'data4', 'data5', ] // 4回目
['data6', 'data7', 'data3', 'data4', 'data5', ] // 5回目  ※上限を5件とした場合、6件目は1件目の場所を上書きする。

そこで、以下のCodePenのように実装したのですが、「test」ボタンを押した時の挙動が、
https://codepen.io/charon1212/pen/zYaLWqY

  • 期待値
    • 1回目… [0,1,2,3,4]と表示される。
    • 2回目… [0,1,2,3,4,0,1,2,3,4]と表示される。
    • 3回目… [1,2,3,4,4,0,1,2,3,4,0]と表示される。
  • 実際
    • 1回目… [4]と表示される。
    • 2回目… [4,null,null,null,null,4]と表示される。
    • 3回目… [4,null,null,null,null,4,null,null,null,null,4]と表示される。

となってしまっています。(※先ほでいう上限は、11件としており、0,1,2,3,4の5件データを複数回登録しています。)

1処理で同じStateのsetStateが複数回呼ばれていることに起因する問題だと思うのですが…
こういう時は、どう実装するのが良いか、知見をお持ちの方がいたらご教授頂きたいです。

該当するソースコード

念のため、CodePenの実装内容を下記に記載します。

const { Component, useState, useEffect, useContext, createContext } = React;

const useCycleArray = <T extends any>(mod: number) => {
  const [list, setList] = useState<T[]>([]);
  const [count, setCount] = useState(0);
  const addList = (item: T) => {
    const copy = [...list];
    copy[count % mod] = item;
    setList(copy);
    setCount((p) => p + 1);
  };
  return [list, addList] as const;
};

const App = () => {
  const [list, addList] = useCycleArray<number>(11);
  const onClick = () => {
    for (let i = 0; i < 5; i++) addList(i);
  };
  return (
    <div>
      <div><button onClick={onClick}>test</button></div>
      <div>{JSON.stringify(list)}</div>
    </div>
  );
};

ReactDOM.render(<App />, document.querySelector(".container"));
0

1Answer

※API問い合わせ1回で、複数のデータが飛んでくる。

のであればaddListの引数は配列になるのでは?ということで以下のような実装でいけると思うのですが、いかがでしょうか?

const { Component, useState, useEffect, useContext, createContext } = React;

const useCycleArray = <T extends any>(mod: number) => {
  const [list, setList] = useState<T[]>([]);
  const [lastIndex, setLastIndex] = useState(0);// APIアクセスごとでどこまで入れたか保持
  const addList = (items: T[]) => {
    const copy = [...list];
    for (let index = 0;index < items.length;index++){
      copy[(lastIndex + index) % mod] = items[index];
    }
    setList(copy);
    setLastIndex((i)=>(i + items.length) % mod);
  };
  return [list, addList] as const;
};

const App = () => {
  const [list, addList] = useCycleArray<number>(11);
  const onClick = () => {
    addList([0, 1, 2, 3, 4]);
  };
  return (
    <div>
      <div><button onClick={onClick}>test</button></div>
      <div>{JSON.stringify(list)}</div>
    </div>
  );
};

ReactDOM.render(<App />, document.querySelector(".container"));

1Like

Comments

  1. @charon1212

    Questioner

    ご回答ありがとうございます!
    確かにこちらで問題なさそうです。引数をリストにする観点は抜けてました…

Your answer might help someone💌