mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
feat: add AI skills pack for Claude Code and OpenCode (#9728)
Co-authored-by: Rubén De la Torre Vico <ruben@prowler.com> Co-authored-by: Adrián Jesús Peña Rodríguez <adrianjpr@gmail.com> Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
122
skills/react-19/SKILL.md
Normal file
122
skills/react-19/SKILL.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
name: react-19
|
||||
description: >
|
||||
React 19 patterns with React Compiler.
|
||||
Trigger: When writing React components - no useMemo/useCallback needed.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: prowler-cloud
|
||||
version: "1.0"
|
||||
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
|
||||
---
|
||||
|
||||
## No Manual Memoization (REQUIRED)
|
||||
|
||||
```typescript
|
||||
// ✅ React Compiler handles optimization automatically
|
||||
function Component({ items }) {
|
||||
const filtered = items.filter(x => x.active);
|
||||
const sorted = filtered.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const handleClick = (id) => {
|
||||
console.log(id);
|
||||
};
|
||||
|
||||
return <List items={sorted} onClick={handleClick} />;
|
||||
}
|
||||
|
||||
// ❌ NEVER: Manual memoization
|
||||
const filtered = useMemo(() => items.filter(x => x.active), [items]);
|
||||
const handleClick = useCallback((id) => console.log(id), []);
|
||||
```
|
||||
|
||||
## Imports (REQUIRED)
|
||||
|
||||
```typescript
|
||||
// ✅ ALWAYS: Named imports
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
// ❌ NEVER
|
||||
import React from "react";
|
||||
import * as React from "react";
|
||||
```
|
||||
|
||||
## Server Components First
|
||||
|
||||
```typescript
|
||||
// ✅ Server Component (default) - no directive
|
||||
export default async function Page() {
|
||||
const data = await fetchData();
|
||||
return <ClientComponent data={data} />;
|
||||
}
|
||||
|
||||
// ✅ Client Component - only when needed
|
||||
"use client";
|
||||
export function Interactive() {
|
||||
const [state, setState] = useState(false);
|
||||
return <button onClick={() => setState(!state)}>Toggle</button>;
|
||||
}
|
||||
```
|
||||
|
||||
## When to use "use client"
|
||||
|
||||
- useState, useEffect, useRef, useContext
|
||||
- Event handlers (onClick, onChange)
|
||||
- Browser APIs (window, localStorage)
|
||||
|
||||
## use() Hook
|
||||
|
||||
```typescript
|
||||
import { use } from "react";
|
||||
|
||||
// Read promises (suspends until resolved)
|
||||
function Comments({ promise }) {
|
||||
const comments = use(promise);
|
||||
return comments.map(c => <div key={c.id}>{c.text}</div>);
|
||||
}
|
||||
|
||||
// Conditional context (not possible with useContext!)
|
||||
function Theme({ showTheme }) {
|
||||
if (showTheme) {
|
||||
const theme = use(ThemeContext);
|
||||
return <div style={{ color: theme.primary }}>Themed</div>;
|
||||
}
|
||||
return <div>Plain</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## Actions & useActionState
|
||||
|
||||
```typescript
|
||||
"use server";
|
||||
async function submitForm(formData: FormData) {
|
||||
await saveToDatabase(formData);
|
||||
revalidatePath("/");
|
||||
}
|
||||
|
||||
// With pending state
|
||||
import { useActionState } from "react";
|
||||
|
||||
function Form() {
|
||||
const [state, action, isPending] = useActionState(submitForm, null);
|
||||
return (
|
||||
<form action={action}>
|
||||
<button disabled={isPending}>
|
||||
{isPending ? "Saving..." : "Save"}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## ref as Prop (No forwardRef)
|
||||
|
||||
```typescript
|
||||
// ✅ React 19: ref is just a prop
|
||||
function Input({ ref, ...props }) {
|
||||
return <input ref={ref} {...props} />;
|
||||
}
|
||||
|
||||
// ❌ Old way (unnecessary now)
|
||||
const Input = forwardRef((props, ref) => <input ref={ref} {...props} />);
|
||||
```
|
||||
Reference in New Issue
Block a user