Я изучал 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, но безрезультатно.
Любая помощь по этому поводу будет принята с благодарностью!