Move state to App component

This commit is contained in:
rui hildt 2020-07-23 11:43:12 +02:00
parent 035eb2e548
commit ce1bd7de40
4 changed files with 121 additions and 97 deletions

View File

@ -1,7 +1,83 @@
import React from 'react'; import React, { useState, useCallback, useEffect } from 'react';
import { Grommet } from 'grommet'; import { Grommet } from 'grommet';
import { debounce } from 'lodash';
import { gql, useLazyQuery } from '@apollo/client';
import { Header } from './Header'; import { Header } from './Header';
import { SearchBox } from './SearchBox'; import { Search } from './Search';
import { Results } from './Results';
import { Artists, Artist } from '../interfaces';
const QUERY_ARTIST_ALBUMS = gql`
query Artist($byName: String!) {
queryArtists(byName: $byName) {
name
image
id
albums {
name
image
id
}
}
}
`;
export default function App() {
const [inputValue, setInputValue] = useState('');
const [getArtists, { data }] = useLazyQuery(QUERY_ARTIST_ALBUMS);
const [artists, setArtists] = useState<Artists | undefined>(undefined);
const [suggestions, setSuggestions] = useState<string[] | undefined>(
undefined,
);
// Debounce the database query
// See: https://archive.is/wip/6JDqb
const updateQuery = () => {
getArtists({ variables: { byName: inputValue } });
};
const delayedQuery = useCallback(debounce(updateQuery, 500), [inputValue]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};
useEffect(() => {
delayedQuery();
// Cancel previous debounce calls during useEffect cleanup.
return delayedQuery.cancel;
}, [inputValue, delayedQuery]);
// TODO: Maybe merge the two use effects?
useEffect(() => {
if (data && data.queryArtists !== []) {
// Limit artists to 5
const updatedArtists = data.queryArtists.slice(0, 5);
const updatedSuggestions: string[] = updatedArtists.map(
(el: Artist) => {
return el.name;
},
);
setSuggestions(updatedSuggestions);
setArtists(updatedArtists);
}
}, [data]);
return (
<Grommet theme={theme}>
<Header>
<h1>Spoti Search</h1>
</Header>
<Search
inputValue={inputValue}
handleChange={handleChange}
suggestions={suggestions}
/>
<Results />
</Grommet>
);
}
const theme = { const theme = {
global: { global: {
@ -12,16 +88,3 @@ const theme = {
}, },
}, },
}; };
function App() {
return (
<Grommet theme={theme}>
<Header>
<h1>Spoti Search</h1>
</Header>
<SearchBox />
</Grommet>
);
}
export default App;

View File

@ -0,0 +1,14 @@
import React, { PropsWithChildren } from 'react';
import { Box } from 'grommet';
export const Results = (props: PropsWithChildren<{}>) => {
return (
<Box
as='section'
direction='row'
justify='center'
margin={{ vertical: 'large' }}
>
</Box>
);
};

29
src/components/Search.tsx Normal file
View File

@ -0,0 +1,29 @@
import React from 'react';
import { Box, TextInput } from 'grommet';
import { FormSearch } from 'grommet-icons';
export const Search = ({
inputValue,
handleChange,
suggestions,
}: {
inputValue: string;
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
suggestions: string[] | undefined;
}) => (
<Box
as='section'
direction='row'
justify='center'
margin={{ vertical: 'large' }}
>
<TextInput
value={inputValue}
onChange={handleChange}
placeholder='Type an artist name'
icon={<FormSearch color='plain' />}
dropHeight='large'
suggestions={suggestions}
/>
</Box>
);

View File

@ -1,82 +0,0 @@
import React, { useState, useCallback, useEffect } from 'react';
import { gql, useLazyQuery } from '@apollo/client';
import { Box, TextInput } from 'grommet';
import { FormSearch } from 'grommet-icons';
import { debounce } from 'lodash';
import { Artists, Artist } from '../interfaces';
const QUERY_ARTIST_ALBUMS = gql`
query Artist($byName: String!) {
queryArtists(byName: $byName) {
name
image
id
albums {
name
image
id
}
}
}
`;
export const SearchBox = () => {
const [inputValue, setInputValue] = useState('');
const [getArtists, { data }] = useLazyQuery(QUERY_ARTIST_ALBUMS);
const [artists, setArtists] = useState<Artists | undefined>(undefined);
const [suggestions, setSuggestions] = useState<string[] | undefined>(
undefined,
);
// Debounce the database query, based on the following article:
// https://dev.to/reflexgravity/use-lodash-debouce-inside-a-functional-component-in-react-4g5j
const updateQuery = () => {
getArtists({ variables: { byName: inputValue } });
};
const delayedQuery = useCallback(debounce(updateQuery, 500), [inputValue]);
const handleChange = (e: any) => {
setInputValue(e.target.value);
};
useEffect(() => {
delayedQuery();
// Cancel previous debounce calls during useEffect cleanup.
return delayedQuery.cancel;
}, [inputValue, delayedQuery]);
// TODO: Maybe merge the two use effects?
useEffect(() => {
if (data && data.queryArtists !== []) {
// Limit artists to 5
const updatedArtists = data.queryArtists.slice(0, 5);
const updatedSuggestions: string[] = updatedArtists.map(
(el: Artist) => {
return el.name;
},
);
setSuggestions(updatedSuggestions);
setArtists(updatedArtists);
}
}, [data]);
return (
<Box
as='section'
direction='row'
justify='center'
margin={{ vertical: 'large' }}
>
<TextInput
value={inputValue}
onChange={handleChange}
placeholder='Type an artist name'
icon={<FormSearch color='plain' />}
dropHeight='large'
suggestions={suggestions}
/>
</Box>
);
};