Back

useAutoAppendCustomProps

A React hook for building multistep forms with ease.

Custom Properties

Hook snippet

Create a file called useAutoAppendCustomProps.tsx with this snippet and import it in your component. and don't forget to adjust the hook according to your object interface.

const useAutoAppendCustomProps = (
  {
    productDetails,
    setProductDetails
  }: {
    productDetails: Product;
    setProductDetails: (productDetails: Product) => void
  }) => {

  const custom = useMemo(() => productDetails.properties.custom || [], [productDetails]);

  useEffect(() => {
    const emptyProps = custom.filter(
      (prop) => !prop.propertyKey?.trim() && !prop.propertyValue?.trim()
    );

    const hasValidLast =
      custom.length === 0 ||
      (custom[custom.length - 1].propertyKey?.trim() &&
        custom[custom.length - 1].propertyValue?.trim());

    let newCustom = [...custom];

    // Remove all empty props except one (keep one empty slot always)
    if (emptyProps.length > 1) {
      let emptyCount = 0;
      newCustom = custom.filter((prop) => {
        const isEmpty = !prop.propertyKey?.trim() && !prop.propertyValue?.trim();
        if (isEmpty) {
          emptyCount++;
          return emptyCount === 1; // Keep only the first empty one
        }
        return true;
      });
    }

    // Add new empty row if the last one is filled
    if (hasValidLast) {
      newCustom.push({ propertyKey: "", propertyValue: "" });
    }

    // Only update if something actually changed
    if (JSON.stringify(newCustom) !== JSON.stringify(custom)) {
      setProductDetails({
        ...productDetails,
        properties: {
          ...productDetails.properties,
          custom: newCustom,
        },
      });
    }
  }, [custom, productDetails, setProductDetails]);
};

Usage

Create an object for initialData.

const initialData = {
  properties: {
    custom: [{ propertyKey: "", propertyValue: "" }],
  };,
};

Import the hook and use it in your component. You can pass the state and state setter as an argument.

const [productDetails, setProductDetails] = useState(initialData);

useAutoAppendCustomProps({ productDetails, setProductDetails });

Helper functions for the hook. Use them for updating the state.

const handleKeyChange = (index: number, newKey: string) => {
  const updated = [...productDetails.properties.custom];
  updated[index].propertyKey = newKey;
  setProductDetails({
    ...productDetails,
    properties: { ...productDetails.properties, custom: updated },
  });
};

const handleValueChange = (index: number, newValue: string) => {
  const updated = [...productDetails.properties.custom];
  updated[index].propertyValue = newValue;
  setProductDetails({
    ...productDetails,
    properties: { ...productDetails.properties, custom: updated },
  });
};

const handleDelete = (index: number) => {
  if (productDetails.properties.custom.length <= 1) return;
  const updated = productDetails.properties.custom.filter((_, i) => i !== index);
  setProductDetails({
    ...productDetails,
    properties: { ...productDetails.properties, custom: updated },
  });
};

Made by Dhvanit. Visit my portfolio.