Реагировать на обновление или добавление в массив объектов в useState при получении нового объекта - PullRequest
0 голосов
/ 11 апреля 2020

Иногда newItem поступает из WebSocket и сохраняется в useState с saveNewItem

, после чего блок useEffect запускается, как и ожидалось.

Обновление. Если в closeArray есть объект с тем же openTime, что и newItem, я хочу заменить этот объект в closeArray на newItem, поскольку у него будет новый close

Добавить. Если в closeArray нет объекта с таким же временем открытия, как у newItem, я хочу добавить sh новый элемент в массив.

Удалить. И наконец, если массив становится длиннее 39 объектов, я хочу удалить первый элемент.

Если я добавлю closeArray к массиву useEffect зависимостей, я собираюсь создать неприятную l oop, если я его не добавлю closeArray не будет обновляться.

Я хочу, чтобы usEffect срабатывал только при изменении newItem, а не при изменении closeArray, но Я все еще хочу получить и установить данные на closeArray в useEffect

interface CloseInterface {
  openTime: number;
  closeTime: number;
  close: number;
}
function App() {
  const [newItem, saveNewItem] = useState<CloseInterface>();
  const [closeArray, saveCloseArray] = useState<CloseInterface[]>([]);

  useEffect(() => {
    if (newItem) {
      let found = false;
      let arr = [];
      for (let i = 0; i < closeArray.length; i++) {
        if (closeArray[i].openTime === newItem.openTime) {
          found = true;
          arr.push(newItem);
        } else {
          arr.push(closeArray[i]);
        }
      }
      if (found === false) {
        arr.push(newItem)
      }
      if (arr.length === 39) arr.shift();
      saveCloseArray(arr);
    }
  }, [newItem]); // <--- I need to add closeArray but it will make a yucky loop

Если я добавлю closeArray в массив зависимостей useEffect, я получу ошибку ...

index.js:1 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
    in App (at src/index.tsx:9)
    in StrictMode (at src/index.tsx:8)

если я не добавлю closeArray в массив зависимостей useEffect, я получаю эту ошибку ...

React Hook useEffect has a missing dependency: 'closeArray'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

второй блок useEffect получает начальные данные для closeArray и слушает WebSocket, который обновляет newItem по мере его поступления.

  useEffect(() => {
    const getDetails = async () => {
      const params = new window.URLSearchParams({
        symbol: symbol.toUpperCase(),
        interval
      });
      const url = `https://api.binance.com/api/v3/klines?${params}&limit=39`;
      const response = await fetch(url, { method: "GET" });
      const data = await response.json();
      if (data) {
        const arrayLength = data.length;
        let newcloseArray = [];
        for (var i = 0; i < arrayLength; i++) {
          const openTime = data[i][0];
          const closeTime = data[i][6];
          const close = data[i][4];
          newcloseArray.push({ openTime, closeTime, close });
        }
        saveCloseArray(newcloseArray);
        const ws = new WebSocket("wss://stream.binance.com:9443/ws");
        ws.onopen = () =>
          ws.send(
            JSON.stringify({
              method: "SUBSCRIBE",
              params: [`${symbol}@kline_${interval}`],
              id: 1
            })
          );
        ws.onmessage = e => {
          const data = JSON.parse(e.data);
          const value = data.k;
          if (value) {
            const openTime = value.t;
            const closeTime = value.T;
            const close = value.c;
            saveNewItem({ openTime, closeTime, close });
          }
        };
      }
    };
    getDetails();
  }, [symbol, interval]);

Ответы [ 3 ]

1 голос
/ 11 апреля 2020

Чтобы лучше написать свой код, вы можете использовать метод обратного вызова средства обновления состояний, чтобы даже если вы не передали closeArray в useEffect, и оно будет иметь обновленные значения при каждом запуске useEffect

function App() {
  const [newItem, saveNewItem] = useState<CloseInterface>();
  const [closeArray, saveCloseArray] = useState<CloseInterface[]>([]);

  useEffect(() => {
    if (newItem) {
      let found = false;

      saveCloseArray(prevCloseArray => {
         let arr = [];
         for (let i = 0; i < prevCloseArray.length; i++) {
            if (prevCloseArray[i].openTime === newItem.openTime) {
              found = true;
              arr.push(newItem);
            } else {
              arr.push(prevCloseArray[i]);
            }
          }
          if (found === false) {
            arr.push(newItem)
          }
          if (arr.length === 39) arr.shift();
          return arr;
      })
    }
  }, [newItem]); 
0 голосов
/ 11 апреля 2020

Если единственная причина, по которой у вас есть newItem, заключается в обновлении closeArray, я бы подумал перенести эту функцию в useEffect, который использует WebSocket. Вы все еще можете использовать newItem, если вам нужно что-то сделать в дополнение к простому обновлению closeArray, например, например, для отображения предупреждения или всплывающего окна. Вот что я имею в виду:

interface CloseInterface {
    openTime: number;
    closeTime: number;
    close: number;
}

function App() {
    const [newItem, saveNewItem] = useState<CloseInterface>();
    const [closeArray, saveCloseArray] = useState<CloseInterface[]>([]);

    useEffect(() => {
        // Do something when newItem changes, e.g. show alert
        if (newItem) {

        }
    }, [newItem]);

    useEffect(() => {
        // Work with the new item
        const precessNewItem = (item = {}) => {
            let found = false;
            let arr = [];

            for (let i = 0; i < closeArray.length; i++) {
                if (closeArray[i].openTime === item.openTime) {
                    found = true;
                    arr.push(item);
                } else {
                    arr.push(closeArray[i]);
                }
            }

            if (found === false) {
                arr.push(item)
            }

            if (arr.length === 39) arr.shift();

            saveCloseArray(arr);

            // save new item 
            saveNewItem(item);
        };

        const getDetails = async () => {
            const params = new window.URLSearchParams({
                symbol: symbol.toUpperCase(),
                interval
            });

            const url = `https://api.binance.com/api/v3/klines?${params}&limit=39`;
            const response = await fetch(url, { method: "GET" });
            const data = await response.json();

            if (data) {
                const arrayLength = data.length;
                let newcloseArray = [];
                for (var i = 0; i < arrayLength; i++) {
                    const openTime = data[i][0];
                    const closeTime = data[i][6];
                    const close = data[i][4];
                    newcloseArray.push({ openTime, closeTime, close });
                }

                saveCloseArray(newcloseArray);
                const ws = new WebSocket("wss://stream.binance.com:9443/ws");

                ws.onopen = () =>
                    ws.send(
                        JSON.stringify({
                            method: "SUBSCRIBE",
                            params: [`${symbol}@kline_${interval}`],
                            id: 1
                        })
                    );

                ws.onmessage = e => {
                    const data = JSON.parse(e.data);
                    const value = data.k;

                    if (value) {
                        const openTime = value.t;
                        const closeTime = value.T;
                        const close = value.c;

                        // process new item
                        processNewItem({ openTime, closeTime, close });
                    }
                };
            }
        };
        getDetails();
    }, [symbol, interval, closeArray]); // add closeArray here
 }
0 голосов
/ 11 апреля 2020

Вы хотите использовать useCallback для сохранения нового массива с обновленным элементом, например, так:

const [closeArray, saveCloseArray] = useState<CloseInterface[]>([]);
const updateEntry = useCallback(newItem => {
  saveCloseArray(oldCloseArray => oldCloseArray.reduce((acc, item) => {
    acc.push(item.openTime === newItem.openTime ? newItem : item);
    return acc;
  }, []));
}, []);

Затем вы примените функцию обратного вызова к вашей кнопке или элементу div или любому другому компоненту. генерируется, EG

return (
  [1, 2, 3, 4, 5].map(item => <button key={`${item}`} onClick={() => updateEntry(item)}>Click me</button>)
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...