Bonus: React State Anti‑Patterns (SPFx)
Common pitfalls that slow down SPFx apps or cause subtle bugs, with practical fixes.
Duplicate derived state
Avoid storing values that can be derived from existing state.
const [items, setItems] = React.useState<string[]>([]);
const filtered = React.useMemo(() => items.filter(i => i.includes(q)), [items, q]);
Mutable updates
Never mutate arrays/objects in place; create new references.
setItems(prev => prev.map(i => (i === old ? next : i)));
setUser(prev => ({ ...prev, name: nextName }));
Index keys in lists
Using indexes as keys breaks identity on reorders. Use stable IDs.
{items.map(item => <Row key={item.id} item={item} />)}
Inline objects/functions causing re-renders
Stabilize props with useMemo/useCallback when rendering large lists.
const columns = React.useMemo(() => [{ key: 'name', name: 'Name' }], []);
const onSelect = React.useCallback((id: string) => setSelected(id), []);
Overloaded Context
Huge context values cause widespread re-renders. Split contexts or use selectors.
const ThemeContext = React.createContext<string>('default');
const UserContext = React.createContext<{ id: string } | null>(null);
setState after unmount
Cancel async work on unmount or property changes.
React.useEffect(() => {
const ac = new AbortController();
(async () => {
const res = await fetch(url, { signal: ac.signal });
// setState safely
})();
return () => ac.abort();
}, [url]);
SPFx notes
- Property pane changes: debounce heavy derivations and network calls.
- Keep SPFx
Contextin containers; presentational components should remain pure. - Virtualize large lists with Fluent UI
List/DetailsList.