From lisa-expo
This skill provides strategies and patterns for reducing cognitive complexity in React components. It should be used when ESLint reports sonarjs/cognitive-complexity violations, when refactoring complex View components, or when planning how to break down large components. The skill enforces this project's Container/View pattern requirements when extracting components.
npx claudepluginhub codyswanngt/lisa --plugin lisa-expoThis skill uses the workspace's default tool permissions.
This skill provides systematic approaches for reducing cognitive complexity in React components while adhering to this project's Container/View pattern requirements.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
This skill provides systematic approaches for reducing cognitive complexity in React components while adhering to this project's Container/View pattern requirements.
sonarjs/cognitive-complexity violations (threshold: 28)Cognitive complexity increases with:
| Source | Complexity Impact | Common in View Components |
|---|---|---|
| Nested conditionals | +1 per nesting | Yes |
| Ternary expressions | +1 each | Yes |
| Logical operators (&&) | +1 each | Yes |
| Loops (map, filter) | +1 each | Yes |
| Switch/case statements | +1 per case | Rare |
| Catch blocks | +1 each | No (Container only) |
| Nested functions | +1 per nesting | Yes |
Before extracting code, determine the appropriate strategy:
// Helper function - no logic, just rendering
function renderSectionHeader(props: {
readonly title: string;
readonly count: number;
}) {
return (
<HStack className="justify-between">
<Text className="font-bold">{props.title}</Text>
<Text className="text-sm">({props.count})</Text>
</HStack>
);
}
FilterChipList/
├── FilterChipListContainer.tsx # Handles selection logic
├── FilterChipListView.tsx # Renders chip list
└── index.tsx # Exports Container
Run ESLint to identify the violation:
bun run lint 2>&1 | grep "cognitive-complexity"
Note: Replace
bunwith your project's package manager (npm,yarn,pnpm) as needed.
Read the file and identify:
Use the decision framework above. For View components:
| Situation | Strategy |
|---|---|
| Repeated JSX, no logic | Helper function |
| Repeated JSX, needs props | Helper function with props object |
| Repeated pattern, 3+ files | Full Container/View component |
| Complex section, own state | Full Container/View component |
| Deeply nested ternaries | Pre-compute flags in Container |
Before refactoring, ensure test coverage exists:
# Check existing coverage
bun run test:unit --coverage --collectCoverageFrom='<file-path>'
If no tests exist, write tests that verify current behavior before refactoring.
For helper functions, see references/extraction-strategies.md.
For full components, use the Container/View pattern skill.
bun run lint 2>&1 | grep "cognitive-complexity"
bun run test:unit
Before (high complexity):
<Pressable
style={{
backgroundColor: selected.includes(item) ? colors.primary : colors.bg,
borderColor: selected.includes(item) ? colors.primary : colors.border,
}}
>
<Text style={{ color: selected.includes(item) ? "#FFF" : colors.text }}>
{item}
</Text>
</Pressable>
After (reduced complexity):
// In Container - pre-compute selection state
const itemStates = useMemo(
() =>
items.map(item => ({
item,
isSelected: selected.includes(item),
})),
[items, selected]
);
// In View - simple conditional
<Pressable style={isSelected ? styles.selected : styles.default}>
<Text style={isSelected ? styles.selectedText : styles.defaultText}>
{item}
</Text>
</Pressable>;
Before (4x repeated pattern = high complexity):
{positions.length > 0 && (
<VStack>
<Text>Positions</Text>
<HStack>{positions.map(p => <Chip key={p} ... />)}</HStack>
</VStack>
)}
{tags.length > 0 && (
<VStack>
<Text>Tags</Text>
<HStack>{tags.map(t => <Chip key={t.id} ... />)}</HStack>
</VStack>
)}
// ... repeated 2 more times
After (extract FilterChipList component):
<FilterChipList
title="Positions"
items={positions}
selectedItems={filters.positions}
onToggle={onPositionToggle}
/>
<FilterChipList
title="Tags"
items={tags}
selectedItems={filters.tags}
onToggle={onTagToggle}
/>
Before:
{
isLoading ? (
<Spinner />
) : hasError ? (
<Error />
) : isEmpty ? (
<Empty />
) : (
<Content />
);
}
After (pre-compute state in Container):
// Container
const viewState = useMemo(() => {
if (isLoading) return "loading";
if (hasError) return "error";
if (isEmpty) return "empty";
return "content";
}, [isLoading, hasError, isEmpty]);
// View - map directly
const VIEW_STATES = {
loading: <Spinner />,
error: <Error />,
empty: <Empty />,
content: <Content />,
} as const;
{
VIEW_STATES[viewState];
}
For detailed patterns and complete examples:
references/extraction-strategies.md - Helper function patterns and when to use eachreferences/refactoring-patterns.md - Step-by-step refactoring examples with before/after code