Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Split Context by responsibility
Separate frequently-changing state from stable actions to reduce consumer re-renders.
Bad example
| 1 | import React, { createContext, useMemo, useState } from "react"; |
| 2 |
|
| 3 | type AppUiCtx = { |
| 4 | isSidebarOpen: boolean; |
| 5 | toggleSidebar: () => void; |
| 6 | toastMessage: string | null; |
| 7 | setToastMessage: (v: string | null) => void; |
| 8 | }; |
| 9 |
|
| 10 | export const AppUiContext = createContext<AppUiCtx | null>(null); |
| 11 |
|
| 12 | export function AppUiProvider({ children }: { children: React.ReactNode }) { |
| 13 | const [isSidebarOpen, setIsSidebarOpen] = useState(false); |
| 14 | const [toastMessage, setToastMessage] = useState<string | null>(null); |
| 15 |
|
| 16 | const value = useMemo<AppUiCtx>( |
| 17 | () => ({ |
| 18 | isSidebarOpen, |
| 19 | toggleSidebar: () => setIsSidebarOpen((v) => !v), |
| 20 | toastMessage, |
| 21 | setToastMessage |
| 22 | }), |
| 23 | [isSidebarOpen, toastMessage] |
| 24 | ); |
| 25 |
|
| 26 | return <AppUiContext.Provider value={value}>{children}</AppUiContext.Provider>; |
| 27 | } |
Explanation (EN)
Unrelated concerns are bundled into one Context. Any change (toast, sidebar) updates the same value and forces all consumers to re-render, even those only needing one part.
Objašnjenje (HR)
Nepovezane brige su u jednom Contextu. Svaka promjena (toast, sidebar) mijenja isti value i tjera sve consumere na rerender, i one kojima treba samo jedan dio.
Good example
| 1 | import React, { createContext, useCallback, useMemo, useState } from "react"; |
| 2 |
|
| 3 | type SidebarState = { isSidebarOpen: boolean }; |
| 4 | type SidebarActions = { toggleSidebar: () => void }; |
| 5 |
|
| 6 | type ToastState = { toastMessage: string | null }; |
| 7 | type ToastActions = { setToastMessage: (v: string | null) => void }; |
| 8 |
|
| 9 | export const SidebarStateContext = createContext<SidebarState | null>(null); |
| 10 | export const SidebarActionsContext = createContext<SidebarActions | null>(null); |
| 11 |
|
| 12 | export const ToastStateContext = createContext<ToastState | null>(null); |
| 13 | export const ToastActionsContext = createContext<ToastActions | null>(null); |
| 14 |
|
| 15 | export function AppUiProvider({ children }: { children: React.ReactNode }) { |
| 16 | const [isSidebarOpen, setIsSidebarOpen] = useState(false); |
| 17 | const [toastMessage, setToastMessage] = useState<string | null>(null); |
| 18 |
|
| 19 | const toggleSidebar = useCallback(function toggleSidebar() { |
| 20 | setIsSidebarOpen((v) => !v); |
| 21 | }, []); |
| 22 |
|
| 23 | const sidebarState = useMemo<SidebarState>(() => ({ isSidebarOpen }), [isSidebarOpen]); |
| 24 | const sidebarActions = useMemo<SidebarActions>(() => ({ toggleSidebar }), [toggleSidebar]); |
| 25 |
|
| 26 | const toastState = useMemo<ToastState>(() => ({ toastMessage }), [toastMessage]); |
| 27 | const toastActions = useMemo<ToastActions>(() => ({ setToastMessage }), []); |
| 28 |
|
| 29 | return ( |
| 30 | <SidebarStateContext.Provider value={sidebarState}> |
| 31 | <SidebarActionsContext.Provider value={sidebarActions}> |
| 32 | <ToastStateContext.Provider value={toastState}> |
| 33 | <ToastActionsContext.Provider value={toastActions}> |
| 34 | {children} |
| 35 | </ToastActionsContext.Provider> |
| 36 | </ToastStateContext.Provider> |
| 37 | </SidebarActionsContext.Provider> |
| 38 | </SidebarStateContext.Provider> |
| 39 | ); |
| 40 | } |
Explanation (EN)
Splitting state and actions (and splitting domains) localizes updates. Consumers subscribe only to what they need, reducing unnecessary rerenders.
Objašnjenje (HR)
Razdvajanje state-a i akcija (i domena) lokalizira promjene. Consumeri se pretplate samo na ono sto im treba i smanjuju se nepotrebni rerenderi.
Notes (EN)
If splitting into multiple contexts is too noisy, consider a reducer-based API where actions are stable and state selectors are scoped.
Bilješke (HR)
Ako je vise contexta previse bucno, razmisli o reducer API-ju gdje su akcije stabilne, a selektori stanja ograniceni.
Exceptions / Tradeoffs (EN)
For small apps with a handful of consumers, a single context may be simpler and acceptable.
Iznimke / Tradeoffi (HR)
Za male aplikacije s malo consumera, jedan context moze biti jednostavniji i prihvatljiv.