An open API service indexing awesome lists of open source software.

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

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