Синхронизация обработчика событий для поиска в React - PullRequest
2 голосов
/ 18 июня 2020

Я изучал js, а затем React. js в течение последних нескольких недель, следуя руководствам по Codecademy и затем Educative.io (чтобы учиться с новыми хуками, а не на основе подхода на основе классов). Пытаясь применить то, что я узнал, я возился с созданием ряда общих функций веб-сайтов в качестве компонентов React в проекте hello-world.

Совсем недавно я пытался создать компонент поиска, который использует Spotify API для поиска трека, но столкнулся с проблемами синхронизации, которые я не могу понять, как решить с помощью js известных мне средств синхронизации. Я происхожу из среды Java, поэтому я больше знаком с мьютексами / семафорами / блокировками / мониторами для чтения и записи, поэтому может быть, что мне не хватает чего-то очевидного. Я основывал код на этой записи блога .

В моей реализации в настоящее время у меня есть компонент SongSearch , которому передается начальный поисковый текст как свойство, а также функция обратного вызова, которая вызывается при изменении входного значения. Он также содержит searchText как состояние, которое используется для изменения значения ввода.

import * as React from 'react';

interface Props {
    initialSearchText: string,
    onSearchTextUpdated: (newSearchText: string) => void;
}

export const SongSearch = (props: Props) => {
    const [searchText, setSearchText] = React.useState(props.initialSearchText);
    const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const newSearchText = e.target.value;
        setSearchText(newSearchText);
        props.onSearchTextUpdated(newSearchText);
    }
    return <input value={searchText} onChange={onChange}/>;
};

Результаты в настоящее время просто отображаются в виде списка в компоненте SearchResults , значения которого передаются в виде массива песен.

import * as React from 'react';

import { SongInfo } from './index';

interface Props {
    songs: SongInfo[]
}

export const SearchResults = (props: Props) => {
    return (
        <ul>
            {props.songs.map((song) => {
                return <li key={song.uri}>{song.name}</li>
            })}
        </ul>
    );
}

In компонент App , я передаю функцию обратного вызова, которая устанавливает атрибут состояния searchText на новое значение. Затем запускается эффект, вызывающий updateSongs (). Если у нас есть токен аутентификации, а текст поиска не пустой, мы возвращаем результаты вызова API, в противном случае мы возвращаем пустой список песен. Результат используется для обновления атрибута треков состояния с помощью setTracks ().

Я урезал код в App.tsx только до соответствующих частей:

import SpotifyWebApi from 'spotify-web-api-js';
import React from "react";

// ... (removed irrelevant code)

async function updateSongs(searchText: string): Promise<SongInfo[]>{
  if (spotify.getAccessToken()) {
    if (searchText === '') {
      console.log('Empty search text.');
      return [];
    } else {
      // if access token has been set 
      const res = await spotify.searchTracks(searchText, {limit: 10});
      const tracks = res.tracks.items.map((trackInfo) => { 
        return {name: trackInfo.name, uri: trackInfo.uri};
      });
      console.log(tracks);
      return tracks;
    }

  } else {
    console.log('Not sending as access token has not yet');
    return [];
  }
}

function App() {
  // ... (removed irrelevant code)

  const initialSearchText = 'Search...';

  const [tracks, setTracks] = React.useState([] as SongInfo[]);

  const [searchText, setSearchText] = React.useState(initialSearchText);

  React.useEffect(() => {
    updateSongs(searchText)
      .then((newSongs) => setTracks(newSongs))
  }, [searchText]);

  const content = <SearchResults songs={tracks}/>;
  return (
    <ThemeProvider theme={theme}>
      <div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
        <Root config={mui_config}>
          <Header
            renderMenuIcon={(open: boolean) => (open ? <ChevronLeft /> : <MenuRounded />)}
          >
            <SongSearch initialSearchText={initialSearchText} onSearchTextUpdated={(newSearchText) =>  {
              console.log(`New Search Text: ${newSearchText}`)
              setSearchText(newSearchText);
            }}/>
          </Header>
          <Nav
            renderIcon={(collapsed: boolean)=>
              collapsed ? <ChevronRight /> : <ChevronLeft />
            }
            classes={drawerStyles}
          >
            Nav
          </Nav>
          <StickyFooter contentBody={content} footerHeight={100} footer={footerContent}/>

        </Root>
      </div>
    </ThemeProvider>
  );
}


export default App;

Проблема, которая У меня возникает то, что, когда я набираю название длинной песни, а затем удерживаю клавишу Backspace, иногда песни остаются отображаться в списке, даже если текст поиска пуст. Изучив журналы консоли в коде, я вижу, что проблема возникает из-за того, что setTracks () иногда вызывается не по порядку, в частности, при быстром удалении 'abcdef' setTracks () результатом updateTracks ('a') будет вызывается после результата updateTracks (''). Это имеет смысл, поскольку '' не требует сетевого трафика c, но я потратил часы, пытаясь понять, как я могу синхронизировать это в javascript, но безрезультатно.

Любая помощь по этому поводу будет принята с благодарностью!

1 Ответ

1 голос
/ 19 июня 2020

В вашем случае результаты возвращаются по-разному, потому что вы отправляете несколько событий, а те, которые приходят первыми, запускают ответ, а затем вы его отображаете.

Моим решением было бы использовать функцию debounce в событии onChange поля ввода. Так что пользователь сначала закончит набирать sh, а затем должен начать поиск. Хотя все еще могут быть некоторые проблемы, если один поиск был запущен, и пользователь начал вводить что-то еще, тогда первый был завершен, а второй начался и закончился. В этом вы можете найти эту отмену запроса полезным. К сожалению, вы не можете отменить Promise, поэтому вам придется прочитать о Rx JS.

Вот рабочий пример с использованием debounce

PS Вы можете найти этот разговор на конференции полезным, чтобы понять, как событие l oop работает в JS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...