111 <PillsInput.Field
112 type="hidden"
113 onBlur={() => combobox.closeDropdown()}
114 onKeyDown={(event) => {115 if (event.key !== "Backspace") return;116117 event.preventDefault();118 handleValueRemove(119 multiSelectValues[multiSelectValues.length - 1]!,120 );121 }}122 />
123 </Combobox.EventsTarget>
124 </Pill.Group>
110 <Combobox.EventsTarget>
111 <PillsInput.Field
112 type="hidden"
113 onBlur={() => combobox.closeDropdown()}114 onKeyDown={(event) => {
115 if (event.key !== "Backspace") return;
116
95 <Combobox.DropdownTarget>
96 <PillsInput
97 pointer
98 onClick={() => combobox.toggleDropdown()} 99 {...props}
100 >
101 <Pill.Group>
89 return (
90 <Combobox
91 store={combobox}
92 onOptionSubmit={handleValueSelect} 93 withinPortal={false}
94 >
95 <Combobox.DropdownTarget>
59 <IntegrationPill
60 key={item}
61 option={data.find((i) => i.id === item)!}
62 onRemove={() => handleValueRemove(item)} 63 />
64 ));
65
152 <Menu.Item
153 c="red.6"
154 leftSection={<IconTrash size={16} />}
155 onClick={openRemoveModal}156 >
157 {t("action.remove")}
158 </Menu.Item>
140 <Menu.Label>{t("menu.label.settings")}</Menu.Label>
141 <Menu.Item
142 leftSection={<IconPencil size={16} />}
143 onClick={openEditModal}144 >
145 {t("action.edit")}
146 </Menu.Item>
66 </Text>
67 </Stack>
68 <Button
69 onClick={() => {70 handleAdd(kind);71 }}72 variant="light"
73 size="xs"
74 mt="auto"
55 size={48}
56 variant="default"
57 radius="xl"
58 onClick={() => {59 return modalEvents.openManagedModal({60 modal: "widgetEditModal",61 innerProps: {62 kind,63 value: state,64 onSuccessfulEdit: (value) => {65 setState(value);66 },67 integrationData: integrationData.filter(68 (integration) =>69 "supportedIntegrations" in currentDefinition &&70 currentDefinition.supportedIntegrations.some(71 (kind) => kind === integration.kind,72 ),73 ),74 integrationSupport:75 "supportedIntegrations" in currentDefinition,76 },77 });78 }}79 >
80 <IconPencil size={24} />
81 </ActionIcon>
134 };
135
136 return (
137 <HeaderButton onClick={toggle} loading={isPending}>138 {isEditMode ? (
139 <IconPencilOff stroke={1.5} />
140 ) : (
80
81 <Menu.Item
82 leftSection={<IconBoxAlignTop size={20} />}
83 onClick={() => 84 modalEvents.openManagedModal({ 85 title: t("section.category.create.title"), 86 modal: "categoryEditModal", 87 innerProps: { 88 submitLabel: t("section.category.create.submit"), 89 category: { 90 id: "new", 91 name: "", 92 }, 93 onSuccess({ name }) { 94 addCategoryToEnd({ name }); 95 }, 96 }, 97 }) 98 } 99 >
100 {t("section.category.action.create")}
101 </Menu.Item>
61 <Menu.Dropdown style={{ transform: "translate(-3px, 0)" }}>
62 <Menu.Item
63 leftSection={<IconBox size={20} />}
64 onClick={() => 65 modalEvents.openManagedModal({ 66 title: t("item.create.title"), 67 size: "xl", 68 modal: "itemSelectModal", 69 innerProps: {}, 70 }) 71 } 72 >
73 {t("item.action.create")}
74 </Menu.Item>
33 leftSection={<IconSearch stroke={1.5} size={20} />}
34 placeholder={t("integration.page.list.search")}
35 value={search}
36 onChange={(e) => setSearch(e.target.value)}37 />
38
39 {filteredKinds.length > 0 ? (
113 <SecretCard
114 key={kind}
115 secret={secretsMap.get(kind)!}
116 onCancel={() =>117 new Promise((res) => {118 // When nothing changed, just close the secret card119 if (120 (form.values.secrets[index]?.value ?? "") ===121 (secretsMap.get(kind)?.value ?? "")122 ) {123 return res(true);124 }125 modalEvents.openConfirmModal({126 title: t("integration.secrets.reset.title"),127 children: t("integration.secrets.reset.message"),128 onCancel: () => res(false),129 onConfirm: () => {130 form.setFieldValue(131 `secrets.${index}.value`,132 secretsMap.get(kind)!.value ?? "",133 );134 res(true);135 },136 });137 })138 }139 >
140 <IntegrationSecretInput
141 label={t(`integration.secrets.kind.${kind}.newLabel`)}
83 <Anchor
84 type="button"
85 component="button"
86 onClick={async () => { 87 await mutateAsync(integration, { 88 onSuccess: () => { 89 removeDirty(); 90 showSuccessNotification({ 91 title: t("notification.success.title"), 92 message: t("notification.success.message"), 93 }); 94 }, 95 onError: (error) => { 96 if (error.data?.zodError?.fieldErrors.url) { 97 showErrorNotification({ 98 title: t("notification.invalidUrl.title"), 99 message: t("notification.invalidUrl.message"),100 });101 return;102 }103104 if (error.message === "SECRETS_NOT_DEFINED") {105 showErrorNotification({106 title: t("notification.notAllSecretsProvided.title"),107 message: t("notification.notAllSecretsProvided.message"),108 });109 return;110 }111112 showErrorNotification({113 title: t("notification.commonError.title"),114 message: t("notification.commonError.message"),115 });116 },117 });118 }}119 >
120 {t("action")}
121 </Anchor>
73 ) : null}
74 <Button
75 variant="default"
76 onClick={async () => {77 if (!editMode) {78 setEditMode(true);79 return;80 }8182 const shouldCancel = await onCancel();83 if (!shouldCancel) return;84 setEditMode(false);85 }}86 >
87 {editMode ? t("common.action.cancel") : t("common.action.edit")}
88 </Button>
31 loading={isPending}
32 variant="subtle"
33 color="red"
34 onClick={() => {35 modalEvents.openConfirmModal({36 title: t("title"),37 children: t("message", integration),38 onConfirm: () => {39 void mutateAsync(40 { id: integration.id },41 {42 onSuccess: () => {43 showSuccessNotification({44 title: t("notification.success.title"),45 message: t("notification.success.message"),46 });47 if (count === 1) {48 router.replace("/integrations");49 }50 void revalidatePathAction("/integrations");51 },52 onError: () => {53 showErrorNotification({54 title: t("notification.error.title"),55 message: t("notification.error.message"),56 });57 },58 },59 );60 },61 });62 }}63 aria-label="Delete integration"
64 >
65 <IconTrash color="red" size={16} stroke={1.5} />
20 <Accordion
21 variant="separated"
22 defaultValue={activeTab}
23 onChange={(tab) =>24 tab25 ? router.replace(`?tab=${tab}`, {})26 : router.replace("/integrations")27 }28 >
29 {children}
30 </Accordion>
Using .bind()
or passing local callback functions as props to react component incurs a performance overhead.
Consider using React.useCallback
, or if possible, moving the callback definition outside the component.
EXCEPTIONS: This rule may not apply if your react component is only rendered once, or if your application is not performance sensitive. In such cases, consider adding a skipcq to prevent DeepSource from raising this issue on a single component. Alternatively, for small applications, you could add this issue in the ignore rules section.
Note that the performance overhead is not determined by the size of the callback function, but instead the number of times the component is rendered.
If the callback passed to a prop is local to the render function, it will get recreated every time the component renders.
This affects performance by causing unnecessary re-renders if a brand new function is passed as a property to a component that uses a reference equality check on the property to determine if it should update.
Using the useCallback
hook on functional components, or a method on class components is more performant.
Bad Practice
function CardWrapper() {
// the function `handleClick` is recreated every time
// a `CardWrapper` component is rendered.
const handleClick = (e) => displayCardDetails(e)
return <Card onClick={handleClick} />
}
function CardWrapper_() {
return <Card onClick={(e) => displayCardDetails(e)} />
}
class _CardWrapper extends React.Component {
render() {
return <Card onClick={(e) => displayCardDetails(e)} />
}
}
Recommended
function CardWrapper() {
// `handleClick` is no longer recreated on every render.
const handleClick = React.useCallback((e) => displayCardDetails(e))
return <Card onClick={handleClick} />
}
class CardWrapper_ extends React.Component {
handleClick(e) {
displayCardDetails(e)
}
render() {
return <Card onClick={this.handleClick} />
}
}