update url only when onblur fires

This commit is contained in:
Jelle van Snik 2022-05-03 20:58:34 +02:00
parent 43b3574834
commit ded99a459e
5 changed files with 66 additions and 19 deletions

View File

@ -1,3 +1,5 @@
import { SearchBarInput } from "components/SearchBar";
import { useSearchQuery } from "hooks/useSearchQuery";
import { MWMediaType } from "providers"; import { MWMediaType } from "providers";
import { Redirect, Route, Switch } from "react-router-dom"; import { Redirect, Route, Switch } from "react-router-dom";
import { BookmarkContextProvider } from "state/bookmark"; import { BookmarkContextProvider } from "state/bookmark";
@ -7,8 +9,17 @@ import "./index.css";
import { MediaView } from "./views/MediaView"; import { MediaView } from "./views/MediaView";
import { SearchView } from "./views/SearchView"; import { SearchView } from "./views/SearchView";
function App() { function TestInput() {
const [q1, c1, b1] = useSearchQuery();
return (
<div>
<p>Normal:</p>
<SearchBarInput onChange={c1} value={q1} onUnFocus={b1} />
</div>
);
}
function App() {
return ( return (
<WatchedContextProvider> <WatchedContextProvider>
<BookmarkContextProvider> <BookmarkContextProvider>
@ -19,6 +30,9 @@ function App() {
<Route exact path="/media/movie/:media" component={MediaView} /> <Route exact path="/media/movie/:media" component={MediaView} />
<Route exact path="/media/series/:media" component={MediaView} /> <Route exact path="/media/series/:media" component={MediaView} />
<Route exact path="/search/:type/:query?" component={SearchView} /> <Route exact path="/search/:type/:query?" component={SearchView} />
<Route exact path="/test/:type/:query?">
<TestInput />
</Route>
<Route path="*" component={NotFoundPage} /> <Route path="*" component={NotFoundPage} />
</Switch> </Switch>
</BookmarkContextProvider> </BookmarkContextProvider>

View File

@ -7,31 +7,39 @@ import { TextInputControl } from "./text-inputs/TextInputControl";
export interface SearchBarProps { export interface SearchBarProps {
buttonText?: string; buttonText?: string;
placeholder?: string; placeholder?: string;
onChange: (value: MWQuery) => void; onChange: (value: MWQuery, force: boolean) => void;
onUnFocus: () => void;
value: MWQuery; value: MWQuery;
} }
export function SearchBarInput(props: SearchBarProps) { export function SearchBarInput(props: SearchBarProps) {
const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false);
function setSearch(value: string) { function setSearch(value: string) {
props.onChange({ props.onChange(
...props.value, {
searchQuery: value, ...props.value,
}); searchQuery: value,
},
false
);
} }
function setType(type: string) { function setType(type: string) {
props.onChange({ props.onChange(
...props.value, {
type: type as MWMediaType, ...props.value,
}); type: type as MWMediaType,
},
true
);
} }
return ( return (
<div className="bg-denim-300 hover:bg-denim-400 focus-within:bg-denim-400 flex flex-col items-center gap-4 rounded-[28px] px-4 py-4 transition-colors sm:flex-row sm:py-2 sm:pl-8 sm:pr-2"> <div className="flex flex-col items-center gap-4 rounded-[28px] bg-denim-300 px-4 py-4 transition-colors focus-within:bg-denim-400 hover:bg-denim-400 sm:flex-row sm:py-2 sm:pl-8 sm:pr-2">
<TextInputControl <TextInputControl
onUnFocus={props.onUnFocus}
onChange={(val) => setSearch(val)} onChange={(val) => setSearch(val)}
value={props.value.searchQuery} value={props.value.searchQuery}
className="placeholder-denim-700 w-full flex-1 bg-transparent text-white focus:outline-none" className="w-full flex-1 bg-transparent text-white placeholder-denim-700 focus:outline-none"
placeholder={props.placeholder} placeholder={props.placeholder}
/> />

View File

@ -1,5 +1,6 @@
export interface TextInputControlPropsNoLabel { export interface TextInputControlPropsNoLabel {
onChange?: (data: string) => void; onChange?: (data: string) => void;
onUnFocus?: () => void;
value?: string; value?: string;
placeholder?: string; placeholder?: string;
className?: string; className?: string;
@ -11,6 +12,7 @@ export interface TextInputControlProps extends TextInputControlPropsNoLabel {
export function TextInputControl({ export function TextInputControl({
onChange, onChange,
onUnFocus,
value, value,
label, label,
className, className,
@ -23,6 +25,7 @@ export function TextInputControl({
placeholder={placeholder} placeholder={placeholder}
onChange={(e) => onChange && onChange(e.target.value)} onChange={(e) => onChange && onChange(e.target.value)}
value={value} value={value}
onBlur={() => onUnFocus && onUnFocus()}
/> />
); );

View File

@ -1,19 +1,25 @@
import { MWMediaType, MWQuery } from "providers"; import { MWMediaType, MWQuery } from "providers";
import React, { useState } from "react"; import React, { useRef, useState } from "react";
import { generatePath, useHistory, useRouteMatch } from "react-router-dom"; import { generatePath, useHistory, useRouteMatch } from "react-router-dom";
export function useSearchQuery(): [MWQuery, (inp: Partial<MWQuery>) => void] { export function useSearchQuery(): [
MWQuery,
(inp: Partial<MWQuery>, force: boolean) => void,
() => void
] {
const history = useHistory(); const history = useHistory();
const isFirstRender = useRef(false);
const { path, params } = useRouteMatch<{ type: string; query: string }>(); const { path, params } = useRouteMatch<{ type: string; query: string }>();
const [search, setSearch] = useState<MWQuery>({ const [search, setSearch] = useState<MWQuery>({
searchQuery: "", searchQuery: "",
type: MWMediaType.MOVIE, type: MWMediaType.MOVIE,
}); });
const updateParams = (inp: Partial<MWQuery>) => { const updateParams = (inp: Partial<MWQuery>, force: boolean) => {
const copySearch: MWQuery = { ...search }; const copySearch: MWQuery = { ...search };
Object.assign(copySearch, inp); Object.assign(copySearch, inp);
setSearch(copySearch); setSearch(copySearch);
if (!force) return;
history.replace( history.replace(
generatePath(path, { generatePath(path, {
query: query:
@ -23,13 +29,28 @@ export function useSearchQuery(): [MWQuery, (inp: Partial<MWQuery>) => void] {
); );
}; };
const onUnFocus = () => {
history.replace(
generatePath(path, {
query: search.searchQuery.length === 0 ? undefined : search.searchQuery,
type: search.type,
})
);
};
// only run on first load of the page
React.useEffect(() => { React.useEffect(() => {
if (isFirstRender.current === true) {
isFirstRender.current = false;
return;
}
isFirstRender.current = true;
const type = const type =
Object.values(MWMediaType).find((v) => params.type === v) || Object.values(MWMediaType).find((v) => params.type === v) ||
MWMediaType.MOVIE; MWMediaType.MOVIE;
const searchQuery = params.query || ""; const searchQuery = params.query || "";
setSearch({ type, searchQuery }); setSearch({ type, searchQuery });
}, [params, setSearch]); }, [setSearch, params, isFirstRender]);
return [search, updateParams]; return [search, updateParams, onUnFocus];
} }

View File

@ -165,7 +165,7 @@ function ExtraItems() {
export function SearchView() { export function SearchView() {
const [searching, setSearching] = useState<boolean>(false); const [searching, setSearching] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [search, setSearch] = useSearchQuery(); const [search, setSearch, setSearchUnFocus] = useSearchQuery();
const debouncedSearch = useDebounce<MWQuery>(search, 2000); const debouncedSearch = useDebounce<MWQuery>(search, 2000);
useEffect(() => { useEffect(() => {
@ -182,7 +182,7 @@ export function SearchView() {
return ( return (
<SearchResultsView <SearchResultsView
searchQuery={debouncedSearch} searchQuery={debouncedSearch}
clear={() => setSearch({ searchQuery: "" })} clear={() => setSearch({ searchQuery: "" }, true)}
/> />
); );
return <ExtraItems />; return <ExtraItems />;
@ -201,6 +201,7 @@ export function SearchView() {
<SearchBarInput <SearchBarInput
onChange={setSearch} onChange={setSearch}
value={search} value={search}
onUnFocus={setSearchUnFocus}
placeholder="What movie do you want to watch?" placeholder="What movie do you want to watch?"
/> />
</div> </div>