-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: remove useForm dependency from BasicToolForm (#987)
* chore: remove useForm dependency from BasicToolForm useForm returns `useRef().current` under the hood as a hack to optimize rendering, which causes some erratic behavior when certain conditions are met (I think it has to do with resetting the form's default values...????) Either way, it added way more complexity than it was worth (at least for this component) so removing it was the best solution * chore: remove redundant add/remove code for Tool Catalog
- Loading branch information
1 parent
aa3405d
commit 01885c2
Showing
5 changed files
with
71 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,105 +1,48 @@ | ||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import { useEffect, useMemo } from "react"; | ||
import { useFieldArray, useForm } from "react-hook-form"; | ||
import { z } from "zod"; | ||
import { memo, useCallback, useMemo, useState } from "react"; | ||
|
||
import { ToolEntry } from "~/components/agent/ToolEntry"; | ||
import { ToolCatalogDialog } from "~/components/tools/ToolCatalog"; | ||
import { Form } from "~/components/ui/form"; | ||
|
||
const formSchema = z.object({ | ||
tools: z.array(z.object({ value: z.string() })), | ||
}); | ||
|
||
type BasicToolFormValues = z.infer<typeof formSchema>; | ||
|
||
type Tools = { tools: string[] }; | ||
|
||
export function BasicToolForm({ | ||
defaultValues: _defaultValues, | ||
onChange, | ||
}: { | ||
defaultValues?: Partial<Tools>; | ||
onSubmit?: (values: Tools) => void; | ||
onChange?: (values: Tools) => void; | ||
export const BasicToolForm = memo(function BasicToolFormComponent(props: { | ||
value?: string[]; | ||
defaultValue?: string[]; | ||
onChange?: (values: string[]) => void; | ||
}) { | ||
const defaultValues = useMemo(() => { | ||
return { | ||
tools: | ||
_defaultValues?.tools?.map((tool) => ({ value: tool })) || [], | ||
}; | ||
}, [_defaultValues]); | ||
|
||
const form = useForm<BasicToolFormValues>({ | ||
resolver: zodResolver(formSchema), | ||
defaultValues: { tools: defaultValues?.tools || [] }, | ||
}); | ||
const { getValues, reset } = form; | ||
|
||
useEffect(() => { | ||
const unchanged = compareArrays( | ||
defaultValues?.tools.map(({ value }) => value) || [], | ||
getValues().tools.map(({ value }) => value) | ||
); | ||
const { onChange } = props; | ||
|
||
if (unchanged) return; | ||
|
||
reset({ tools: defaultValues?.tools || [] }); | ||
}, [defaultValues, getValues, reset]); | ||
|
||
const toolArr = useFieldArray({ control: form.control, name: "tools" }); | ||
|
||
useEffect(() => { | ||
return form.watch((values) => { | ||
const { data, success } = formSchema.safeParse(values); | ||
|
||
if (!success) return; | ||
const [_value, _setValue] = useState(props.defaultValue); | ||
const value = useMemo( | ||
() => props.value ?? _value ?? [], | ||
[props.value, _value] | ||
); | ||
|
||
onChange?.({ tools: data.tools.map((t) => t.value) }); | ||
}).unsubscribe; | ||
}, [form, onChange]); | ||
const setValue = useCallback( | ||
(newValue: string[]) => { | ||
_setValue(newValue); | ||
onChange?.(newValue); | ||
}, | ||
[onChange] | ||
); | ||
|
||
const removeTools = (toolsToRemove: string[]) => { | ||
const indexes = toolsToRemove | ||
.map((tool) => toolArr.fields.findIndex((t) => t.value === tool)) | ||
.filter((index) => index !== -1); | ||
|
||
toolArr.remove(indexes); | ||
}; | ||
|
||
const addTool = (tool: string) => { | ||
toolArr.append({ value: tool }); | ||
setValue(value.filter((tool) => !toolsToRemove.includes(tool))); | ||
}; | ||
|
||
return ( | ||
<Form {...form}> | ||
<div className="flex flex-col gap-2"> | ||
<div className="flex flex-col gap-1 w-full overflow-y-auto"> | ||
{toolArr.fields.map((field) => ( | ||
<ToolEntry | ||
key={field.id} | ||
tool={field.value} | ||
onDelete={() => removeTools([field.value])} | ||
/> | ||
))} | ||
</div> | ||
|
||
<div className="flex justify-end"> | ||
<ToolCatalogDialog | ||
tools={toolArr.fields.map((field) => field.value)} | ||
onAddTool={addTool} | ||
onRemoveTools={removeTools} | ||
<div className="flex flex-col gap-2"> | ||
<div className="flex flex-col gap-1 w-full overflow-y-auto"> | ||
{value.map((tool) => ( | ||
<ToolEntry | ||
key={tool} | ||
tool={tool} | ||
onDelete={() => removeTools([tool])} | ||
/> | ||
</div> | ||
))} | ||
</div> | ||
</Form> | ||
); | ||
} | ||
|
||
function compareArrays(a: string[], b: string[]) { | ||
const aSet = new Set(a); | ||
|
||
if (aSet.size !== b.length) return false; | ||
|
||
return b.every((tool) => aSet.has(tool)); | ||
} | ||
<div className="flex justify-end"> | ||
<ToolCatalogDialog tools={value} onUpdateTools={setValue} /> | ||
</div> | ||
</div> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters