At Factory, we have a simple, yet important frontend rule: no...
```typescript
export function useMountEffect(effect: () => void | (() => void)) {
/* eslint-disable no-restricted-syntax */
useEffect(effect, []);
}
``````typescript
// ❌ BAD: Two render cycles - first stale, then filtered
function ProductList() {
const [products, setProducts] = useState([]);
const [filteredProducts, setFilteredProducts] = useState([]);
useEffect(() => {
setFilteredProducts(products.filter((p) => p.inStock));
}, [products]);
}
// ✅ GOOD: Compute inline in one render
function ProductList() {
const [products, setProducts] = useState([]);
const filteredProducts = products.filter((p) => p.inStock);
}
``````typescript
// ❌ BAD: total in deps can loop
function Cart({ subtotal }) {
const [tax, setTax] = useState(0);
const [total, setTotal] = useState(0);
useEffect(() => {
setTax(subtotal * 0.1);
}, [subtotal]);
useEffect(() => {
setTotal(subtotal + tax);
}, [subtotal, tax, total]);
}
// ✅ GOOD: No effects required
function Cart({ subtotal }) {
const tax = subtotal * 0.1;
const total = subtotal + tax;
}
``````typescript
// ❌ BAD: Race condition risk
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
useEffect(() => {
fetchProduct(productId).then(setProduct);
}, [productId]);
}
// ✅ GOOD: Query library handles cancellation/caching/staleness
function ProductPage({ productId }) {
const { data: product } = useQuery(['product', productId], () =>
fetchProduct(productId)
);
}
``````typescript
// ❌ BAD: Effect as an action relay
function LikeButton() {
const [liked, setLiked] = useState(false);
useEffect(() => {
if (liked) {
postLike();
setLiked(false);
}
}, [liked]);
return ;
}
// ✅ GOOD: Direct event-driven action
function LikeButton() {
return ;
}
``````typescript
function useMountEffect(callback: () => void | (() => void)) {
useEffect(callback, []);
}
``````typescript
// ❌ BAD: Guard inside effect
function VideoPlayer({ isLoading }) {
useEffect(() => {
if (!isLoading) playVideo();
}, [isLoading]);
}
// ✅ GOOD: Mount only when preconditions are met
function VideoPlayerWrapper({ isLoading }) {
if (isLoading) return ;
return ;
}
function VideoPlayer() {
useMountEffect(() => playVideo());
}
// ✅ ALSO GOOD: Persistent shell + conditional instance
function VideoPlayerInstance() {
useMountEffect(() => playVideo());
}
function VideoPlayerContainer({ isLoading }) {
return (
<>
{!isLoading && }
>
);
}
``````typescript
// ❌ BAD: Effect attempts to emulate remount behavior
function VideoPlayer({ videoId }) {
useEffect(() => {
loadVideo(videoId);
}, [videoId]);
}
// ✅ GOOD: key forces clean remount
function VideoPlayer({ videoId }) {
useMountEffect(() => {
loadVideo(videoId);
});
}
function VideoPlayerWrapper({ videoId }) {
return ;
}
```


