https://github.com/codemeasandwich/app-data
React/Webpack - File based state management
https://github.com/codemeasandwich/app-data
Last synced: 6 months ago
JSON representation
React/Webpack - File based state management
- Host: GitHub
- URL: https://github.com/codemeasandwich/app-data
- Owner: codemeasandwich
- License: mit
- Created: 2025-09-18T11:20:35.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2025-09-18T11:43:41.000Z (10 months ago)
- Last Synced: 2025-09-20T11:35:06.253Z (10 months ago)
- Language: JavaScript
- Size: 9.77 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: ReadMe.md
- License: LICENSE
Awesome Lists containing this project
README
# app-data (aka context-auto)
A file-system based state management library for React that uses convention over configuration to eliminate boilerplate.
## Overview
`context-auto` is a unique state management solution that leverages your project's directory structure to automatically create and wire up state slices, actions, and reducers. Built on React's Context API, it provides a powerful and intuitive developer experience with zero boilerplate.
## Features
- **Zero Boilerplate**: No action types, action creators, or manual store configuration
- **File-System Based**: Your folder structure defines your state shape and actions
- **Immutable State**: State exposed via Proxy to prevent accidental mutations
- **First-Class Async Support**: Built-in handling for async operations with automatic loading states
- **Lazy Loading**: Support for on-demand data fetching
- **TypeScript Ready**: Full TypeScript support (if applicable)
- **Developer Friendly**: Intuitive API that feels natural to use
## Quick Start
### 1. Setup the Provider
Wrap your app with the `StateProvider`:
```jsx
// src/App.js
import React from 'react';
import { StateProvider } from 'app-data';
import MyComponent from './MyComponent';
function App() {
return (
);
}
export default App;
```
### 2. Create Your State Structure
Organize your state using folders. Each folder represents a state slice:
```
/src
/store
/user
INDEX.js # Initial state & reducer
login.js # Synchronous action
fetchProfile.js # Async action
/cart
INDEX.js # Initial state & reducer
addItem.js # Synchronous action
checkout.js # Async action
```
### 3. Use in Components
```jsx
import useStore from 'app-data';
function MyComponent() {
const store = useStore();
// Access state
const user = store.user;
// Call actions
const handleLogin = () => {
store.user.login({ username: 'john' });
};
// Check async loading states
const isLoading = store.user.loading.fetchProfile;
return (
Welcome {user.name}
{isLoading && Loading...}
Login
);
}
```
## File Conventions
### INDEX.js - Initial State
Each state slice must have an `INDEX.js` file that exports a default function returning the initial state:
```javascript
// store/user/INDEX.js
export default function() {
return {
name: '',
email: '',
isAuthenticated: false
};
}
```
### Action Files
#### Synchronous Actions
Export a single default function that receives the current state and payload:
```javascript
// store/user/login.js
export default function(state, payload) {
return {
...state,
name: payload.name,
isAuthenticated: true
};
}
```
#### Asynchronous Actions
Export four named functions to handle async lifecycle:
```javascript
// store/user/fetchProfile.js
export function action(payload) {
// Return a promise or undefined to cancel
return fetch(`/api/user/${payload.id}`)
.then(res => res.json());
}
export function pending(state, payload) {
// State while loading
return { ...state, loading: true };
}
export function fulfilled(state, payload, response) {
// State on success
return {
...state,
...response,
loading: false
};
}
export function rejected(state, payload, error) {
// State on error
return {
...state,
error: error.message,
loading: false
};
}
```
### Special Files
#### INIT.js - Startup Actions
Runs automatically when the app loads:
```javascript
// store/user/INIT.js
export default async function() {
const token = localStorage.getItem('token');
if (token) {
return await validateToken(token);
}
}
```
#### DEFER.js - Lazy Loading
Fetches data the first time the state slice is accessed:
```javascript
// store/products/DEFER.js
export default async function() {
return await fetch('/api/products')
.then(res => res.json());
}
```
## Loading States
Async actions automatically track their loading state:
```javascript
const store = useStore();
// Before action is called
store.user.loading.save // undefined
// During action
store.user.loading.save // true
// After success
store.user.loading.save // false
// After error
store.user.loading.save // Error object
// Clear loading state
store.user.save.clear();
// or
store.user.loading.save.clear();
```
## Advanced Usage
### Accessing Multiple State Slices
```javascript
const store = useStore();
const { user, cart, products } = store;
```
### Conditional Actions
```javascript
const store = useStore();
if (!store.user.isAuthenticated) {
store.user.login(credentials);
}
```
### Error Handling
```javascript
const store = useStore();
const error = store.user.loading.save;
if (error instanceof Error) {
console.error('Save failed:', error.message);
error.clear(); // Reset error state
}
```
## API Reference
### ``
Wraps your app to provide state management.
### `useStore()`
Hook that returns the store object with all state slices and actions.
### State Slice Properties
- Direct access to state values
- `.loading.[actionName]` - Loading state for async actions
- Action methods automatically attached
### Action Methods
- `store.[slice].[action](payload)` - Execute action
- `store.[slice].[action].clear()` - Clear async action state
## Best Practices
1. Keep state slices focused and single-purpose
2. Use INIT.js for authentication checks
3. Use DEFER.js for expensive initial data loads
4. Name actions descriptively (verb + noun)
5. Handle errors gracefully in rejected functions
## License
MIT