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 { Redirect, Route, Switch } from "react-router-dom";
import { BookmarkContextProvider } from "state/bookmark";
@ -7,8 +9,17 @@ import "./index.css";
import { MediaView } from "./views/MediaView";
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 (
<WatchedContextProvider>
<BookmarkContextProvider>
@ -19,6 +30,9 @@ function App() {
<Route exact path="/media/movie/:media" component={MediaView} />
<Route exact path="/media/series/:media" component={MediaView} />
<Route exact path="/search/:type/:query?" component={SearchView} />
<Route exact path="/test/:type/:query?">
<TestInput />
</Route>
<Route path="*" component={NotFoundPage} />
</Switch>
</BookmarkContextProvider>

View File

@ -7,31 +7,39 @@ import { TextInputControl } from "./text-inputs/TextInputControl";
export interface SearchBarProps {
buttonText?: string;
placeholder?: string;
onChange: (value: MWQuery) => void;
onChange: (value: MWQuery, force: boolean) => void;
onUnFocus: () => void;
value: MWQuery;
}
export function SearchBarInput(props: SearchBarProps) {
const [dropdownOpen, setDropdownOpen] = useState(false);
function setSearch(value: string) {
props.onChange({
...props.value,
searchQuery: value,
});
props.onChange(
{
...props.value,
searchQuery: value,
},
false
);
}
function setType(type: string) {
props.onChange({
...props.value,
type: type as MWMediaType,
});
props.onChange(
{
...props.value,
type: type as MWMediaType,
},
true
);
}
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
onUnFocus={props.onUnFocus}
onChange={(val) => setSearch(val)}
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}
/>

View File

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

View File

@ -1,19 +1,25 @@
import { MWMediaType, MWQuery } from "providers";
import React, { useState } from "react";
import React, { useRef, useState } from "react";
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 isFirstRender = useRef(false);
const { path, params } = useRouteMatch<{ type: string; query: string }>();
const [search, setSearch] = useState<MWQuery>({
searchQuery: "",
type: MWMediaType.MOVIE,
});
const updateParams = (inp: Partial<MWQuery>) => {
const updateParams = (inp: Partial<MWQuery>, force: boolean) => {
const copySearch: MWQuery = { ...search };
Object.assign(copySearch, inp);
setSearch(copySearch);
if (!force) return;
history.replace(
generatePath(path, {
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(() => {
if (isFirstRender.current === true) {
isFirstRender.current = false;
return;
}
isFirstRender.current = true;
const type =
Object.values(MWMediaType).find((v) => params.type === v) ||
MWMediaType.MOVIE;
const searchQuery = params.query || "";
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() {
const [searching, setSearching] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const [search, setSearch] = useSearchQuery();
const [search, setSearch, setSearchUnFocus] = useSearchQuery();
const debouncedSearch = useDebounce<MWQuery>(search, 2000);
useEffect(() => {
@ -182,7 +182,7 @@ export function SearchView() {
return (
<SearchResultsView
searchQuery={debouncedSearch}
clear={() => setSearch({ searchQuery: "" })}
clear={() => setSearch({ searchQuery: "" }, true)}
/>
);
return <ExtraItems />;
@ -201,6 +201,7 @@ export function SearchView() {
<SearchBarInput
onChange={setSearch}
value={search}
onUnFocus={setSearchUnFocus}
placeholder="What movie do you want to watch?"
/>
</div>