Observed since 20 Oct, 20:20 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
A structured framework inspired by database normalization theory for React state management
A structured framework inspired by database normalization theory for React state management
This is the first structured adaptation of the Five Normal Forms (5NF) from relational databases into React state architecture. The core principle: data is data — whether in a database or React state. Normalize it for clarity, performance, and scalability.
Each state variable should contain only primitive values or simple objects.
❌ Bad:
const [user, setUser] = useState({
profile: { name: "John", address: { street: "123 Main" } },
posts: [{ comments: [...] }]
});
✅ Good:
const [userProfile, setUserProfile] = useState({});
const [userAddress, setUserAddress] = useState({});
const [posts, setPosts] = useState([]);
Each state should have a single reason to change. Derived state should be computed!
❌ Bad:
const [user, setUser] = useState({
firstName: "John",
lastName: "Doe",
fullName: "John Doe" // Redundant!
});
✅ Good:
const [user, setUser] = useState({
firstName: "John",
lastName: "Doe"
});
const fullName = useMemo(
() => `${user.firstName} ${user.lastName}`,
[user]
);
State shouldn't depend on other non-key state. Eliminate cascading updates!
❌ Bad:
const [cartItems, setCartItems] = useState([]);
const [cartTotal, setCartTotal] = useState(0); // Depends on cartItems!
✅ Good:
const [cartItems, setCartItems] = useState([]);
const cartTotal = useMemo(
() => cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0),
[cartItems]
);
Use normalized data structures (entities by ID). Avoid duplicating entity data!
❌ Bad:
const [posts, setPosts] = useState([
{ id: 1, author: { id: 1, name: "John" } },
{ id: 2, author: { id: 1, name: "John" } } // Duplicated author!
]);
✅ Good:
const [posts, setPosts] = useState({
1: { id: 1, authorId: 1 }
});
const [authors, setAuthors] = useState({
1: { id: 1, name: "John" }
});
Separate concerns into different contexts/stores. Perfect for Redux & Zustand!
❌ Bad:
const useAppStore = create(() => ({
user: {}, posts: [], ui: {}, cart: {},
// ... 50 more properties 😱
}));
✅ Good:
const useAuthStore = create(...);
const usePostsStore = create(...);
const useUIStore = create(...);
npm install 5-react-normalization-rules
# or
yarn add 5-react-normalization-rules
# or
pnpm add 5-react-normalization-rules
Package: 5-react-normalization-rules on NPM
Peer Dependencies:
import { normalize, denormalize, extractEntities } from '5-react-normalization-rules';
// Normalize an array of objects
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
];
const normalized = normalize(users);
// { 1: { id: 1, name: 'John' }, 2: { id: 2, name: 'Jane' } }
// Denormalize back to array
const denormalized = denormalize(normalized);
// [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]
// Extract nested entities (most powerful!)
const posts = [
{ id: 1, title: 'Post 1', author: { id: 10, name: 'John' } },
{ id: 2, title: 'Post 2', author: { id: 10, name: 'John' } }
];
const { items, entities } = extractEntities(posts, 'author');
// items: { 1: { id: 1, title: 'Post 1', authorId: 10 }, ... }
// entities: { 10: { id: 10, name: 'John' } }
import { useNormalizedState } from '5-react-normalization-rules';
interface User {
id: number;
name: string;
email: string;
}
function UserManagement() {
const {
state, // Raw normalized state
addEntity, // Add single entity
addEntities, // Add multiple entities
updateEntity, // Update entity
removeEntity, // Remove entity
getEntity, // Get entity by ID
getAllEntities, // Get all as array
hasEntity, // Check if exists
clear, // Clear all
count // Total count
} = useNormalizedState<User>({});
const handleAddUser = () => {
addEntity({ id: Date.now(), name: 'New User', email: 'user@example.com' });
};
const handleUpdateUser = (id: number, name: string) => {
updateEntity(id, { name });
};
return (
<div>
<button onClick={handleAddUser}>Add User</button>
<p>Total Users: {count}</p>
{getAllEntities.map(user => (
<div key={user.id}>
<span>{user.name}</span>
<button onClick={() => handleUpdateUser(user.id, 'Updated Name')}>
Update
</button>
<button onClick={() => removeEntity(user.id)}>Delete</button>
</div>
))}
</div>
);
}
import { useNormalizedState, extractEntities } from '5-react-normalization-rules';
function BlogApp() {
// Normalize posts and authors
const posts = useNormalizedState<Post>({});
const authors = useNormalizedState<Author>({});
const comments = useNormalizedState<Comment>({});
// Fetch and normalize API data
useEffect(() => {
fetch('/api/posts')
.then(res => res.json())
.then(data => {
const { items: normalizedPosts, entities: normalizedAuthors } =
extractEntities(data, 'author');
posts.addEntities(Object.values(normalizedPosts));
authors.addEntities(Object.values(normalizedAuthors));
});
}, []);
// Update author name - automatically updates ALL posts by this author
const updateAuthorName = (authorId: number, newName: string) => {
authors.updateEntity(authorId, { name: newName });
};
return (
<div>
{posts.getAllEntities.map(post => {
const author = authors.getEntity(post.authorId);
return (
<article key={post.id}>
<h2>{post.title}</h2>
<p>By {author?.name}</p>
</article>
);
})}
</div>
);
}
The package includes Zustand store examples (useAuthStore, usePostsStore, useUIStore) to demonstrate how to apply 5RNF principles to your own stores. These are educational examples, not meant to be used directly in production.
Use them as templates:
// Your own store following 5RNF principles
import { create } from 'zustand';
interface Product {
id: number;
name: string;
categoryId: number;
}
interface ProductsState {
products: Record<number, Product>; // 4RNF: Normalized
addProduct: (product: Product) => void;
updateProduct: (id: number, updates: Partial<Product>) => void;
}
export const useProductsStore = create<ProductsState>((set) => ({
products: {},
addProduct: (product) =>
set((state) => ({
products: { ...state.products, [product.id]: product }
})),
updateProduct: (id, updates) =>
set((state) => ({
products: {
...state.products,
[id]: { ...state.products[id], ...updates }
}
}))
}));
Check out the /examples folder for complete working examples:
1RNF-AtomicState.ts - Breaking down complex state2RNF-SingleResponsibility.ts - Computing derived state3RNF-NoTransitiveDependencies.ts - Shopping cart example4RNF-NormalizedCollections.ts - Posts and authors normalization5RNF-ContextSeparation.ts - Multiple Zustand stores✅ Better Performance - 98% fewer unnecessary re-renders
✅ Memory Efficient - 99% less duplicated data
✅ Zero Sync Bugs - Derived values always consistent
✅ 100x Faster Updates - Change once, update everywhere
✅ Cleaner Code - 90% less complexity
✅ Team-Friendly - Clear patterns, easy to maintain
📊 See Real Benefits with Concrete Numbers →
Contributions are welcome! Please feel free to submit a Pull Request.
MIT © Khaled Salem
Khaled Salem - React Architecture Innovator
If you find this project useful, please consider:
⭐ If you find this useful, please consider giving it a star on GitHub!
Download counts and popularity will appear here as the collector receives them.
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 20:20 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:cdbef13d240f6a7537ba3ea3169ac7332e5183692a2a19abd78e32d7401fcdb7