mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
chore(ai-workflow): track repo-managed review tooling (#1046)
* chore(ai-workflow): track repo-managed review tooling * fix(ai-workflow): remove repo-specific path assumptions Make shared workflow hooks and APK testing guidance resolve paths from the repo and contributor environment so the tooling works for all contributors, not just one machine.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
---
|
||||
name: you-might-not-need-an-effect
|
||||
description: Analyze code for useEffect anti-patterns and refactor to simpler alternatives. Use when the user says "you might not need an effect", "check effects", "useEffect audit", or asks to review useEffect usage.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# You Might Not Need an Effect
|
||||
|
||||
Analyze code for `useEffect` anti-patterns and refactor to simpler, more correct alternatives.
|
||||
|
||||
Based on https://react.dev/learn/you-might-not-need-an-effect
|
||||
|
||||
## Arguments
|
||||
|
||||
- **scope**: what to analyze (default: uncommitted changes). Examples: `diff to main`, `src/components/`, `whole codebase`
|
||||
- **fix**: whether to apply fixes (default: `true`). Set to `false` to only propose changes.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Determine scope** — get the relevant code:
|
||||
- Default: `git diff` for uncommitted changes
|
||||
- If a directory/file is specified, read those files
|
||||
- If "whole codebase": search all `.tsx`/`.ts` files for `useEffect`
|
||||
|
||||
2. **Scan for anti-patterns** — check each `useEffect` against the patterns below
|
||||
|
||||
3. **Fix or propose** — depending on the `fix` argument:
|
||||
- `fix=true`: apply the refactors, then verify with `yarn build && yarn lint && yarn type-check`
|
||||
- `fix=false`: list each anti-pattern found with a before/after code suggestion
|
||||
|
||||
4. **Report** — summarize what was found and changed
|
||||
|
||||
## Anti-Patterns to Catch
|
||||
|
||||
### 1. Deriving state during render (no effect needed)
|
||||
|
||||
If you're computing something from existing props or state, calculate it during render.
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern
|
||||
const [fullName, setFullName] = useState('');
|
||||
useEffect(() => {
|
||||
setFullName(firstName + ' ' + lastName);
|
||||
}, [firstName, lastName]);
|
||||
|
||||
// ✅ Fix — derive during render
|
||||
const fullName = firstName + ' ' + lastName;
|
||||
```
|
||||
|
||||
### 2. Caching expensive calculations (useMemo, not useEffect)
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern
|
||||
const [filtered, setFiltered] = useState([]);
|
||||
useEffect(() => {
|
||||
setFiltered(items.filter(item => item.active));
|
||||
}, [items]);
|
||||
|
||||
// ✅ Fix — calculate during render (useMemo only if profiling shows it's needed)
|
||||
const filtered = items.filter(item => item.active);
|
||||
```
|
||||
|
||||
### 3. Resetting state when props change (use key, not useEffect)
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern
|
||||
useEffect(() => {
|
||||
setComment('');
|
||||
}, [postCid]);
|
||||
|
||||
// ✅ Fix — use key on the component to reset state
|
||||
<CommentForm key={postCid} />
|
||||
```
|
||||
|
||||
### 4. Fetching data (use bitsocial-react-hooks, not useEffect)
|
||||
|
||||
This project uses `bitsocial-react-hooks` for all data fetching. Never use `useEffect` + `fetch`.
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern
|
||||
const [comment, setComment] = useState(null);
|
||||
useEffect(() => {
|
||||
fetchComment(cid).then(setComment);
|
||||
}, [cid]);
|
||||
|
||||
// ✅ Fix — use the hook
|
||||
const { state, ...comment } = useComment({ commentCid: cid });
|
||||
```
|
||||
|
||||
### 5. Syncing with external stores (use Zustand, not useEffect)
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern
|
||||
const [theme, setTheme] = useState('light');
|
||||
useEffect(() => {
|
||||
const unsub = settingsStore.subscribe((s) => setTheme(s.theme));
|
||||
return unsub;
|
||||
}, []);
|
||||
|
||||
// ✅ Fix — use the Zustand store directly
|
||||
const theme = useSettingsStore((s) => s.theme);
|
||||
```
|
||||
|
||||
### 6. Sending analytics / logging (move to event handlers)
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern — fires on every render, not on user action
|
||||
useEffect(() => {
|
||||
logPageView(pageName);
|
||||
}, [pageName]);
|
||||
|
||||
// ✅ Fix — call in the event handler or route change callback
|
||||
const navigate = () => {
|
||||
logPageView(pageName);
|
||||
router.push(path);
|
||||
};
|
||||
```
|
||||
|
||||
### 7. Initializing global singletons (use module scope or lazy init)
|
||||
|
||||
```typescript
|
||||
// ❌ Anti-pattern
|
||||
useEffect(() => {
|
||||
initializeAnalytics();
|
||||
}, []);
|
||||
|
||||
// ✅ Fix — module-level init (runs once on import)
|
||||
if (typeof window !== 'undefined') {
|
||||
initializeAnalytics();
|
||||
}
|
||||
```
|
||||
|
||||
## Project-Specific Replacements
|
||||
|
||||
| useEffect pattern | Replace with |
|
||||
|-------------------|-------------|
|
||||
| Fetch data | `useComment`, `useFeed`, `useSubplebbit`, etc. from bitsocial-react-hooks |
|
||||
| Sync shared state | Zustand store in `src/stores/` |
|
||||
| Derive values from state | Calculate during render |
|
||||
| Boolean loading/error flags | `state` field from bitsocial-react-hooks, or state machine in Zustand |
|
||||
|
||||
## When useEffect IS Appropriate
|
||||
|
||||
Not every effect is wrong. Keep `useEffect` for:
|
||||
- Subscribing to browser APIs (resize, intersection observer, etc.) with proper cleanup
|
||||
- Synchronizing with non-React systems (third-party widgets, imperative DOM)
|
||||
- Running code on mount that genuinely has no hook equivalent
|
||||
Reference in New Issue
Block a user