Skip to content

Commit 163ad45

Browse files
Fixing small bugs (#17037)
* 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 * Linting fixes * rewrite config for connect demo app * Removing demo app rewrite rule This wasn't gonna work
1 parent a1688d2 commit 163ad45

File tree

4 files changed

+59
-20
lines changed

4 files changed

+59
-20
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+
- Fixing the SelectApp component to properly handle controlled values when not found in search results
8+
- Fixing 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: 29 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,28 @@ 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+
}, [
35+
inputValue,
36+
]);
37+
2138
const {
2239
isLoading,
2340
// TODO error
@@ -29,7 +46,11 @@ export function SelectApp({
2946
Option,
3047
SingleValue,
3148
} = components;
32-
const selectedValue = apps?.find((o) => o.name_slug === value?.name_slug) || null;
49+
// If we have a value prop but it's not in the search results, use the value prop directly
50+
const selectedValue = apps?.find((o) => o.name_slug === value?.name_slug)
51+
|| (value?.name_slug
52+
? value as AppResponse
53+
: null);
3354
return (
3455
<Select
3556
instanceId={instanceId}
@@ -86,8 +107,11 @@ export function SelectApp({
86107
getOptionValue={(o) => o.name_slug}
87108
value={selectedValue}
88109
onChange={(o) => onChange?.((o as AppResponse) || undefined)}
89-
onInputChange={(v) => {
90-
if (v) setQ(v)
110+
onInputChange={(v, { action }) => {
111+
// Only update on user input, not on blur/menu-close/etc
112+
if (action === "input-change") {
113+
setInputValue(v)
114+
}
91115
}}
92116
isLoading={isLoading}
93117
/>

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

Lines changed: 24 additions & 14 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";
@@ -101,7 +101,7 @@ export const FormContextProvider = <T extends ConfigurableProps>({
101101
const [
102102
sdkErrors,
103103
setSdkErrors,
104-
] = useState<SdkError[]>([])
104+
] = useState<SdkError[]>([]);
105105

106106
const [
107107
enabledOptionalProps,
@@ -187,20 +187,30 @@ 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 = Array.isArray(props)
204+
? props.slice(0, reloadPropIdx + 1) as unknown as T // eslint-disable-line react/prop-types
205+
: props; // XXX
206+
}
207+
return props;
208+
}, [
209+
dynamicProps?.configurableProps,
210+
formProps.component.configurable_props,
211+
propNames,
212+
reloadPropIdx,
213+
]);
204214

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

0 commit comments

Comments
 (0)