I am trying to make a search bar with a clear button using the useSubmit hook from react-router. The search query should be submitted both on input change and button click.
SearchBar.tsx
import { Form, useSubmit } from 'react-router-dom';
import { ChangeEvent, useState } from 'react';
export default function SearchBar({ query }: { query?: string }) {
const submit = useSubmit();
const [q, setQ] = useState(query ?? '');
function handleChange(event: ChangeEvent<HTMLInputElement>) {
setQ(event.target.value);
submitForm(event.currentTarget.form);
}
function handleClear(event: any) {
setQ('');
submitForm(event.currentTarget.form);
}
function submitForm(submitTarget: any) {
const isFirstSearch = q == null;
submit(submitTarget, {
replace: !isFirstSearch,
});
}
return (
<Form role="search">
<input
type="search"
name="q"
value={q}
onChange={handleChange}
autoComplete="off"
/>
{q && q.length > 0 && (
<button type="button" onClick={handleClear}>
Clear
</button>
)}
</Form>
);
}
todos.tsx
import { getTodos } from '../todos';
import { useLoaderData } from 'react-router-dom';
import SearchBar from '../SearchBar';
export async function loader({ request }: { request: Request }) {
const url = new URL(request.url);
const q = url.searchParams.get('q');
const todos = await getTodos(q);
return { todos, q };
}
export default function Todos() {
const { todos, q }: any = useLoaderData();
return (
<div>
<SearchBar query={q} />
<div>
{todos.length > 0 ? (
todos.map((todo: any) => <div key={todo.id}>{todo.text}</div>)
) : (
<p>No todos</p>
)}
</div>
</div>
);
}
When I click the clear button, the input gets cleared but the submitted query has still the value from before.
Here's what I was able to come up with that worked for the two use cases you describe.
submitForm
to the Form
component's onChange
handler to submit the form data when it changes.input
element's onChange
handler only updates the local q
state and the value
prop is removed to now be an uncontrolled input. We only need the q
state to conditionally render the "clear" button.type="submit"
to then also submit the form when clicked.handleClear
handler should reset the form field data.export default function SearchBar({ query }: Props) {
const submit = useSubmit();
const [q, setQ] = useState(query ?? '');
function handleChange(event: ChangeEvent<HTMLInputElement>) {
setQ(event.target.value);
}
function handleClear(event: any) {
event.currentTarget.form.reset();
}
function submitForm(event: any) {
const isFirstSearch = q == null;
submit(event.currentTarget, {
replace: !isFirstSearch,
});
}
return (
<Form role="search" onChange={submitForm}>
<input
type="search"
name="q"
onChange={handleChange}
autoComplete="off"
/>
{!!q.length && (
<button type="submit" onClick={handleClear}>
Clear
</button>
)}
</Form>
);
}