Skip to content

Commit 522f33b

Browse files
Fixing small bugs
- Fix SelectApp component to properly handle controlled values when not found in search results - Fix infinite re-render issue in ComponentFormContainer by memoizing configurableProps
1 parent 0184878 commit 522f33b

File tree

4 files changed

+47
-19
lines changed

4 files changed

+47
-19
lines changed

packages/connect-react/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
# Changelog
44

5+
# [1.2.1] - 2025-06-07
6+
7+
- Fix SelectApp component to properly handle controlled values when not found in search results
8+
- Fix infinite re-render issue in ComponentFormContainer by memoizing configurableProps
9+
510
# [1.2.0] - 2025-06-05
611

712
- Adding basic support for 'sql' prop types

packages/connect-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/connect-react",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "Pipedream Connect library for React",
55
"files": [
66
"dist"

packages/connect-react/src/components/SelectApp.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
useId, useState,
2+
useId, useState, useEffect,
33
} from "react";
44
import Select, { components } from "react-select";
55
import { useApps } from "../hooks/use-apps";
@@ -13,11 +13,26 @@ type SelectAppProps = {
1313
export function SelectApp({
1414
value, onChange,
1515
}: SelectAppProps) {
16+
const [
17+
inputValue,
18+
setInputValue,
19+
] = useState("");
1620
const [
1721
q,
1822
setQ,
19-
] = useState(""); // XXX can we just use Select ref.value instead?
23+
] = useState(""); // Debounced query value
24+
2025
const instanceId = useId();
26+
27+
// Debounce the search query
28+
useEffect(() => {
29+
const timer = setTimeout(() => {
30+
setQ(inputValue);
31+
}, 300); // 300ms delay
32+
33+
return () => clearTimeout(timer);
34+
}, [inputValue]);
35+
2136
const {
2237
isLoading,
2338
// TODO error
@@ -29,7 +44,9 @@ export function SelectApp({
2944
Option,
3045
SingleValue,
3146
} = components;
32-
const selectedValue = apps?.find((o) => o.name_slug === value?.name_slug) || null;
47+
// If we have a value prop but it's not in the search results, use the value prop directly
48+
const selectedValue = apps?.find((o) => o.name_slug === value?.name_slug) ||
49+
(value?.name_slug ? value as AppResponse : null);
3350
return (
3451
<Select
3552
instanceId={instanceId}
@@ -86,8 +103,11 @@ export function SelectApp({
86103
getOptionValue={(o) => o.name_slug}
87104
value={selectedValue}
88105
onChange={(o) => onChange?.((o as AppResponse) || undefined)}
89-
onInputChange={(v) => {
90-
if (v) setQ(v)
106+
onInputChange={(v, { action }) => {
107+
// Only update on user input, not on blur/menu-close/etc
108+
if (action === "input-change") {
109+
setInputValue(v)
110+
}
91111
}}
92112
isLoading={isLoading}
93113
/>

packages/connect-react/src/hooks/form-context.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
createContext, useContext, useEffect, useId, useState, type ReactNode,
2+
createContext, useContext, useEffect, useId, useMemo, useState, type ReactNode,
33
} from "react";
44
import isEqual from "lodash.isequal";
55
import { useQuery } from "@tanstack/react-query";
@@ -187,20 +187,23 @@ export const FormContextProvider = <T extends ConfigurableProps>({
187187
]);
188188

189189
// XXX fix types of dynamicProps, props.component so this type decl not needed
190-
let configurableProps: T = dynamicProps?.configurableProps || formProps.component.configurable_props || [];
191-
if (propNames?.length) {
192-
const _configurableProps = [];
193-
for (const prop of configurableProps) {
194-
// TODO decided propNames (and hideOptionalProps) should NOT filter dynamic props
195-
if (propNames.findIndex((name) => prop.name === name) >= 0) {
196-
_configurableProps.push(prop);
190+
const configurableProps = useMemo<T>(() => {
191+
let props: T = dynamicProps?.configurableProps || formProps.component.configurable_props || [];
192+
if (propNames?.length) {
193+
const _configurableProps = [];
194+
for (const prop of props) {
195+
// TODO decided propNames (and hideOptionalProps) should NOT filter dynamic props
196+
if (propNames.findIndex((name) => prop.name === name) >= 0) {
197+
_configurableProps.push(prop);
198+
}
197199
}
200+
props = _configurableProps as unknown as T; // XXX
198201
}
199-
configurableProps = _configurableProps as unknown as T; // XXX
200-
}
201-
if (reloadPropIdx != null) {
202-
configurableProps = configurableProps.slice(0, reloadPropIdx + 1) as unknown as T; // XXX
203-
}
202+
if (reloadPropIdx != null) {
203+
props = props.slice(0, reloadPropIdx + 1) as unknown as T; // XXX
204+
}
205+
return props;
206+
}, [dynamicProps?.configurableProps, formProps.component.configurable_props, propNames, reloadPropIdx]);
204207

205208
// these validations are necessary because they might override PropInput for number case for instance
206209
// so can't rely on that base control form validation

0 commit comments

Comments
 (0)