Skip to content

Commit

Permalink
[Security Solution][Detection Engione] memoize SelectRuleType compone…
Browse files Browse the repository at this point in the history
…nt on rule edit/create page (elastic#161012)

## Summary

Small improvement, part of
elastic#159380
Memoizing SelectRuleType saves ~16-20ms, around 25% faster for form
re-render

### Before
<img width="2236" alt="Screenshot 2023-06-30 at 14 53 23"
src="https://github.com/elastic/kibana/assets/92328789/c0a468ae-eac1-43f0-8c4d-2f2aadaefb8c">




### After
<img width="2247" alt="Screenshot 2023-06-30 at 14 55 28"
src="https://github.com/elastic/kibana/assets/92328789/5a0caea8-0698-43a1-b186-6a1f343edcb6">
  • Loading branch information
vitaliidm authored Jul 17, 2023
1 parent d5996d7 commit 55bb58b
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, memo } from 'react';
import { EuiCard, EuiFlexGrid, EuiFlexItem, EuiFormRow, EuiIcon } from '@elastic/eui';

import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
Expand All @@ -29,164 +29,162 @@ interface SelectRuleTypeProps {
isUpdateView: boolean;
}

export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({
describedByIds = [],
field,
isUpdateView,
hasValidLicense,
isMlAdmin,
}) => {
const ruleType = field.value as Type;
const setType = useCallback(
(type: Type) => {
field.setValue(type);
},
[field]
);
const setEql = useCallback(() => setType('eql'), [setType]);
const setMl = useCallback(() => setType('machine_learning'), [setType]);
const setQuery = useCallback(() => setType('query'), [setType]);
const setThreshold = useCallback(() => setType('threshold'), [setType]);
const setThreatMatch = useCallback(() => setType('threat_match'), [setType]);
const setNewTerms = useCallback(() => setType('new_terms'), [setType]);
export const SelectRuleType: React.FC<SelectRuleTypeProps> = memo(
({ describedByIds = [], field, isUpdateView, hasValidLicense, isMlAdmin }) => {
const ruleType = field.value as Type;
const setType = useCallback(
(type: Type) => {
field.setValue(type);
},
[field]
);
const setEql = useCallback(() => setType('eql'), [setType]);
const setMl = useCallback(() => setType('machine_learning'), [setType]);
const setQuery = useCallback(() => setType('query'), [setType]);
const setThreshold = useCallback(() => setType('threshold'), [setType]);
const setThreatMatch = useCallback(() => setType('threat_match'), [setType]);
const setNewTerms = useCallback(() => setType('new_terms'), [setType]);

const eqlSelectableConfig = useMemo(
() => ({
onClick: setEql,
isSelected: isEqlRule(ruleType),
}),
[ruleType, setEql]
);
const eqlSelectableConfig = useMemo(
() => ({
onClick: setEql,
isSelected: isEqlRule(ruleType),
}),
[ruleType, setEql]
);

const querySelectableConfig = useMemo(
() => ({
onClick: setQuery,
isSelected: isQueryRule(ruleType),
}),
[ruleType, setQuery]
);
const querySelectableConfig = useMemo(
() => ({
onClick: setQuery,
isSelected: isQueryRule(ruleType),
}),
[ruleType, setQuery]
);

const mlSelectableConfig = useMemo(
() => ({
isDisabled: !hasValidLicense || !isMlAdmin,
onClick: setMl,
isSelected: isMlRule(ruleType),
}),
[ruleType, setMl, hasValidLicense, isMlAdmin]
);
const mlSelectableConfig = useMemo(
() => ({
isDisabled: !hasValidLicense || !isMlAdmin,
onClick: setMl,
isSelected: isMlRule(ruleType),
}),
[ruleType, setMl, hasValidLicense, isMlAdmin]
);

const thresholdSelectableConfig = useMemo(
() => ({
onClick: setThreshold,
isSelected: isThresholdRule(ruleType),
}),
[ruleType, setThreshold]
);
const thresholdSelectableConfig = useMemo(
() => ({
onClick: setThreshold,
isSelected: isThresholdRule(ruleType),
}),
[ruleType, setThreshold]
);

const threatMatchSelectableConfig = useMemo(
() => ({
onClick: setThreatMatch,
isSelected: isThreatMatchRule(ruleType),
}),
[ruleType, setThreatMatch]
);
const threatMatchSelectableConfig = useMemo(
() => ({
onClick: setThreatMatch,
isSelected: isThreatMatchRule(ruleType),
}),
[ruleType, setThreatMatch]
);

const newTermsSelectableConfig = useMemo(
() => ({
onClick: setNewTerms,
isSelected: isNewTermsRule(ruleType),
}),
[ruleType, setNewTerms]
);
const newTermsSelectableConfig = useMemo(
() => ({
onClick: setNewTerms,
isSelected: isNewTermsRule(ruleType),
}),
[ruleType, setNewTerms]
);

return (
<EuiFormRow
fullWidth
data-test-subj="selectRuleType"
describedByIds={describedByIds}
label={field.label}
>
<EuiFlexGrid columns={3}>
{(!isUpdateView || querySelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="customRuleType"
title={i18n.QUERY_TYPE_TITLE}
titleSize="xs"
description={i18n.QUERY_TYPE_DESCRIPTION}
icon={<EuiIcon size="xl" type="search" />}
selectable={querySelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || mlSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="machineLearningRuleType"
title={i18n.ML_TYPE_TITLE}
titleSize="xs"
description={<MlCardDescription hasValidLicense={hasValidLicense} />}
icon={<EuiIcon size="l" type="machineLearningApp" />}
isDisabled={mlSelectableConfig.isDisabled && !mlSelectableConfig.isSelected}
selectable={mlSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || thresholdSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="thresholdRuleType"
title={i18n.THRESHOLD_TYPE_TITLE}
titleSize="xs"
description={i18n.THRESHOLD_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="indexFlush" />}
selectable={thresholdSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || eqlSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="eqlRuleType"
title={i18n.EQL_TYPE_TITLE}
titleSize="xs"
description={i18n.EQL_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="eql" />}
selectable={eqlSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || threatMatchSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="threatMatchRuleType"
title={i18n.THREAT_MATCH_TYPE_TITLE}
titleSize="xs"
description={i18n.THREAT_MATCH_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="list" />}
selectable={threatMatchSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || newTermsSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="newTermsRuleType"
title={i18n.NEW_TERMS_TYPE_TITLE}
titleSize="xs"
description={i18n.NEW_TERMS_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="magnifyWithPlus" />}
selectable={newTermsSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
</EuiFlexGrid>
</EuiFormRow>
);
};
return (
<EuiFormRow
fullWidth
data-test-subj="selectRuleType"
describedByIds={describedByIds}
label={field.label}
>
<EuiFlexGrid columns={3}>
{(!isUpdateView || querySelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="customRuleType"
title={i18n.QUERY_TYPE_TITLE}
titleSize="xs"
description={i18n.QUERY_TYPE_DESCRIPTION}
icon={<EuiIcon size="xl" type="search" />}
selectable={querySelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || mlSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="machineLearningRuleType"
title={i18n.ML_TYPE_TITLE}
titleSize="xs"
description={<MlCardDescription hasValidLicense={hasValidLicense} />}
icon={<EuiIcon size="l" type="machineLearningApp" />}
isDisabled={mlSelectableConfig.isDisabled && !mlSelectableConfig.isSelected}
selectable={mlSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || thresholdSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="thresholdRuleType"
title={i18n.THRESHOLD_TYPE_TITLE}
titleSize="xs"
description={i18n.THRESHOLD_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="indexFlush" />}
selectable={thresholdSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || eqlSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="eqlRuleType"
title={i18n.EQL_TYPE_TITLE}
titleSize="xs"
description={i18n.EQL_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="eql" />}
selectable={eqlSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || threatMatchSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="threatMatchRuleType"
title={i18n.THREAT_MATCH_TYPE_TITLE}
titleSize="xs"
description={i18n.THREAT_MATCH_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="list" />}
selectable={threatMatchSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
{(!isUpdateView || newTermsSelectableConfig.isSelected) && (
<EuiFlexItem>
<EuiCard
data-test-subj="newTermsRuleType"
title={i18n.NEW_TERMS_TYPE_TITLE}
titleSize="xs"
description={i18n.NEW_TERMS_TYPE_DESCRIPTION}
icon={<EuiIcon size="l" type="magnifyWithPlus" />}
selectable={newTermsSelectableConfig}
layout="horizontal"
/>
</EuiFlexItem>
)}
</EuiFlexGrid>
</EuiFormRow>
);
}
);

SelectRuleType.displayName = 'SelectRuleType';
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,16 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
[indexPattern]
);

const selectRuleTypeProps = useMemo(
() => ({
describedByIds: ['detectionEngineStepDefineRuleType'],
isUpdateView,
hasValidLicense: hasMlLicense(mlCapabilities),
isMlAdmin: hasMlAdminPermissions(mlCapabilities),
}),
[isUpdateView, mlCapabilities]
);

return (
<>
<StepContentWrapper addPadding={!isUpdateView}>
Expand All @@ -673,12 +683,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
<UseField
path="ruleType"
component={SelectRuleType}
componentProps={{
describedByIds: ['detectionEngineStepDefineRuleType'],
isUpdateView,
hasValidLicense: hasMlLicense(mlCapabilities),
isMlAdmin: hasMlAdminPermissions(mlCapabilities),
}}
componentProps={selectRuleTypeProps}
/>
<RuleTypeEuiFormRow $isVisible={!isMlRule(ruleType)} fullWidth>
<>
Expand Down

0 comments on commit 55bb58b

Please sign in to comment.