Skip to content

[BUG] Issues with pre-configured props in form state management #18778

@rnijhara

Description

@rnijhara

Hey team! I've been integrating @pipedream/connect-react into our product and encountered several challenges with handling pre-configured props. I wanted to share what I found and the solutions that worked for us, in case this helps improve the library.

Our Use Case

Users can configure Pipedream component forms (e.g., Google Sheets actions, Slack messages) and save those configurations to database. Later, when trigger events happen, we execute those actions with the saved props. Users can also come back and edit their saved configurations, which means we need to:

  1. Store configuredProps from the form
  2. Repopulate the form with those stored values when users want to edit
  3. Have everything work correctly with dynamic props, remote options, optional fields, etc.

This workflow revealed some unexpected behaviors that I wanted to document with potential fixes.


Issue 1: Pre-configured props don't load remote options 🔴

Impact: Critical - Forms don't work when editing saved configurations

What we observed:
When mounting <ComponentFormContainer> with configuredProps that include an app selection, dependent fields with remoteOptions show empty dropdowns instead of fetching their options.

How to reproduce:

<ComponentFormContainer
  component={{
    key: 'google_sheets-update-row',
    // ... other component props
  }}
  configuredProps={{
    googleSheets: { authProvisionId: "apn_xxx" }
  }}
  externalUserId="test-user"
/>

Expected: Spreadsheet ID dropdown should fetch and show list of spreadsheets
Actual: Dropdown shows "No options"

Root cause:
In form-context.tsx line 384, the queryDisabledIdx initialization uses _configuredProps (the empty internal state) instead of the actual configuredProps parameter passed to the component. This causes all remote option queries to be blocked when mounting with pre-configured values.

Suggested fix:

// Current implementation:
useEffect(() => {
  updateConfiguredPropsQueryDisabledIdx(_configuredProps)
}, [component.key]);

// Proposed change:
useEffect(() => {
  updateConfiguredPropsQueryDisabledIdx(configuredProps)
}, [component.key]);

Issue 2: Optional props with values don't auto-enable 🔴

Impact: Critical - Saved data appears lost to users

What we observed:
When you save a form with optional fields filled in, then remount the form with those saved configuredProps, the optional fields don't appear in the form - they stay hidden.

How to reproduce:

// User fills optional "Drive" field and saves
const saved = {
  googleSheets: { authProvisionId: "apn_xxx" },
  drive: "My Drive",  // optional field with value
  sheetId: { __lv: { label: "My Sheet", value: "sheet123" } }
};

// Later, remount with saved props
<ComponentFormContainer configuredProps={saved} />
// The "Drive" field is hidden even though it has a saved value

Root cause:
In form-context.tsx line 167, enabledOptionalProps resets to {} when component.key changes, but there's no logic to re-enable optional fields that have values in the configuredProps.

Suggested fix:
Add a useEffect that scans configuredProps on mount and automatically enables optional props with values:

useEffect(() => {
  const propsToEnable: Record<string, boolean> = {};
  
  for (const prop of configurableProps) {
    if (prop.optional) {
      const value = configuredProps[prop.name as keyof ConfiguredProps<T>];
      if (value !== undefined) {
        propsToEnable[prop.name] = true;
      }
    }
  }
  
  if (Object.keys(propsToEnable).length > 0) {
    setEnabledOptionalProps(prev => ({
      ...prev,
      ...propsToEnable,
    }));
  }
}, [component.key, configurableProps, configuredProps]);

Issue 3: Optional prop values lost during sync 🟡

Impact: High - Silent data loss during dynamic props reload

What we observed:
When dynamic props reload (e.g., after selecting a worksheet that triggers reloadProps), values from disabled optional props get lost.

Root cause:
The sync effect in form-context.tsx around line 412-420 has a continue statement for disabled optional props without preserving their existing values first.

Suggested fix:

if (prop.optional && !optionalPropIsEnabled(prop)) {
  // Preserve the value even if the optional prop is disabled
  const value = configuredProps[prop.name as keyof ConfiguredProps<T>];
  if (value !== undefined) {
    newConfiguredProps[prop.name as keyof ConfiguredProps<T>] = value;
  }
  continue;
}

Issue 4: Dropdown options accumulate instead of replacing 🟡

Impact: High - Wrong options shown, confusing UX

What we observed:
When you change a parent dropdown field (like changing "Channel Type" from "Channels" to "Direct Messages" in Slack), the child dropdown shows options from BOTH queries mixed together instead of replacing them.

How to reproduce:

  1. Select Channel Type = "Channels"
  2. Channel dropdown shows: ["#general", "#random", ...]
  3. Change Channel Type = "Direct Messages"
  4. Channel dropdown shows: ["#general", "#random", "@user1", "@user2", ...]

Expected: Should only show ["@user1", "@user2", ...]

Root cause:
In RemoteOptionsContainer.tsx, the pageable.data array always appends new options without detecting if this is a fresh query vs pagination.

Suggested fix:
Implement page-based logic - when page === 0 (fresh query), replace options; when page > 0 (pagination), append options.


Issue 5: Duplicate API calls when selecting options 🟡

Impact: Medium - Performance concern, potential stale data

What we observed:
Every time you select an option from a dropdown, two /configure API calls fire:

  1. First call: configured_props: {} (empty)
  2. Second call: configured_props: {field: value} (correct)

Root cause:
In form-context.tsx around line 92-94, the setConfiguredProp function calls updateConfiguredPropsQueryDisabledIdx synchronously, which updates queryDisabledIdx state immediately. But setConfiguredProps schedules an async state update. This causes children to re-render twice with mismatched state.

Suggested fix:
Remove the synchronous call and move the queryDisabledIdx update to a reactive useEffect that watches configuredProps changes.


Issue 6: Integer dropdown values get deleted on dynamic props reload 🔴

Impact: Critical - Users cannot complete forms with integer remote options

What we observed:
With Google Sheets component:

  1. Select a spreadsheet → works
  2. Select a worksheet from dropdown → value appears
  3. See "Loading dynamic props..."
  4. Worksheet field clears back to empty

Root cause:
Remote options store integer values in label-value format: {__lv: {label: "Alpha - Dev", value: 1628247051}}. But the sync effect in form-context.tsx line 428 checks prop.type === "integer" && typeof value !== "number" and deletes the value because it's an object, not recognizing this as the correct format for remote options.

Suggested fix:

if (prop.type === "integer" && typeof value !== "number") {
  // Preserve label-value format from remote options dropdowns
  if (!(value && typeof value === "object" && "__lv" in value)) {
    delete newConfiguredProps[prop.name as keyof ConfiguredProps<T>];
  } else {
    newConfiguredProps[prop.name as keyof ConfiguredProps<T>] = value;
  }
}

Testing & Solutions

I've tested all these scenarios extensively with:

  • Google Sheets "Update Row" component
  • Slack "Send Message" component
  • Various combinations of pre-configured props
  • Dynamic props reload scenarios
  • Optional props enabled/disabled

I've implemented fixes for all these issues in a fork. I'll be raising a PR shortly with these changes if that would be helpful, or happy to discuss alternative approaches if there's a different design intention I'm missing.

Environment

  • @pipedream/connect-react: 2.0.0
  • React: 18.3.1
  • Next.js: 15
  • Use case: Production app with persistent form state

Happy to provide more details or clarification on any of these issues. Thanks for the great library!

Metadata

Metadata

Assignees

No one assigned

    Labels

    tracked internallyIssue is also tracked in our internal issue trackertriagedFor maintainers: This issue has been triaged by a Pipedream employee

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions