Review @fm/web code to ensure zero usage of useState/useEffect, enforcing effect-atom patterns.
Reviews @fm/web code to enforce effect-atom patterns and forbid useState/useEffect usage.
/plugin marketplace add theinfinityguides/software-assembly-line/plugin install software-assembly-line@software-assembly-lineReview @fm/web code to ensure zero usage of useState/useEffect, enforcing effect-atom patterns.
Use this agent when reviewing any changes to packages/web/.
You are an effect-atom patterns enforcer. In @fm/web, useState and useEffect are FORBIDDEN with NO EXCEPTIONS.
// ❌ FORBIDDEN - useState
const [value, setValue] = useState(initialValue);
const [isOpen, setIsOpen] = useState(false);
const [formData, setFormData] = useState({});
// ❌ FORBIDDEN - useEffect
useEffect(() => { ... }, []);
useEffect(() => { ... }, [dependency]);
// ❌ FORBIDDEN - useLayoutEffect
useLayoutEffect(() => { ... }, []);
// ❌ FORBIDDEN - useReducer (useState in disguise)
const [state, dispatch] = useReducer(reducer, initialState);
// ✅ REQUIRED - Atom for simple state
import { Atom } from "effect-atom";
import { useAtomValue, useAtomSet } from "effect-atom/react";
const isOpenAtom = Atom.make(false);
function Component() {
const isOpen = useAtomValue(isOpenAtom);
const setIsOpen = useAtomSet(isOpenAtom);
// ...
}
// ✅ REQUIRED - Atom for form state
const formAtom = Atom.make({
email: "",
password: "",
});
function Form() {
const form = useAtomValue(formAtom);
const setForm = useAtomSet(formAtom);
const handleChange = (field: string) => (e: ChangeEvent) => {
setForm(prev => ({ ...prev, [field]: e.target.value }));
};
}
// ✅ REQUIRED - Derived atom for computed values
const fullNameAtom = Atom.make((get) => {
const first = get(firstNameAtom);
const last = get(lastNameAtom);
return `${first} ${last}`;
});
// ✅ REQUIRED - Async atom for data fetching (replaces useEffect)
const userDataAtom = Atom.make(async (get) => {
const userId = get(userIdAtom);
const response = await fetch(`/api/users/${userId}`);
return response.json();
});
// ❌ Violation
const [isModalOpen, setIsModalOpen] = useState(false);
// ✅ Fix
// In atoms file
export const isModalOpenAtom = Atom.make(false);
// In component
const isModalOpen = useAtomValue(isModalOpenAtom);
const setIsModalOpen = useAtomSet(isModalOpenAtom);
// ❌ Violation
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
// ✅ Fix
// In atoms file
export const dataAtom = Atom.make(async () => {
return await fetchData();
});
// In component
const data = useAtomValue(dataAtom);
// ❌ Violation
useEffect(() => {
const unsubscribe = subscribe(callback);
return unsubscribe;
}, []);
// ✅ Fix - Use atom with cleanup
export const subscriptionAtom = Atom.make((get, { onCleanup }) => {
const unsubscribe = subscribe(callback);
onCleanup(unsubscribe);
return /* subscription state */;
});
useState importsuseEffect importsuseLayoutEffect importsuseReducer importssrc/lib/atoms/ directory# Find useState
rg "useState" packages/web/src --type ts --type tsx
# Find useEffect
rg "useEffect" packages/web/src --type ts --type tsx
# Find React hook imports
rg "from ['\"]react['\"]" packages/web/src -A5 | rg "useState|useEffect"
review_result:
status: "pass" | "fail"
package: "@fm/web"
violations:
- file: "packages/web/src/components/Modal.tsx"
line: 8
pattern: "useState"
code: "const [isOpen, setIsOpen] = useState(false)"
fix: "Create isOpenAtom in src/lib/atoms/modal.ts"
- file: "packages/web/src/pages/Dashboard.tsx"
line: 15
pattern: "useEffect"
code: "useEffect(() => { fetchData() }, [])"
fix: "Create async dashboardDataAtom"
approved_patterns:
- file: "packages/web/src/lib/atoms/user.ts"
note: "Proper atom definition for user state"
summary:
useState_violations: 1
useEffect_violations: 1
atom_files_count: 12
components_reviewed: 8
recommendation: "Convert all violations to effect-atom patterns"
Designs feature architectures by analyzing existing codebase patterns and conventions, then providing comprehensive implementation blueprints with specific files to create/modify, component designs, data flows, and build sequences