npm package: @funtools/store
Description: Lightweight external store for React, React Native and Next.js
Latest version: 1.0.4

# @funtools/store

> A simple and lightweight state management library for React apps

[![npm version](https://img.shields.io/npm/v/@funtools/store.svg)](https://www.npmjs.com/package/@funtools/store)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

## 📋 What is @funtools/store?

`@funtools/store` is an easy-to-use state management library for React. Think of it as a smarter way to share data between your components without the complexity of Redux or other heavy tools.

**Perfect for beginners and experienced developers alike!**

## ✨ Why Choose @funtools/store?

- ✅ **Super Easy to Learn** - Get started in minutes, not hours
- ✅ **Automatic Features** - Get built-in functions for free (no need to write repetitive code)
- ✅ **TypeScript Friendly** - Get helpful suggestions as you type
- ✅ **Very Small** - Won't bloat your app size
- ✅ **Fast Performance** - Components only update when they need to
- ✅ **Works Everywhere** - React, React Native, and Next.js

## 📦 Installation

Choose your favorite package manager:

```bash
npm install @funtools/store
```

## 🚀 Quick Start - Your First Store

Let's create a simple counter in 3 easy steps:

### Step 1: Create Your Store

```tsx
import { createStore } from "@funtools/store";

// Create a store with initial values
const counterStore = createStore({
    states: {
        count: 0, // Our counter starts at 0
    },
});
```

### Step 2: Use It in a Component

```tsx
function Counter() {
    // Get the count value
    const count = counterStore.useStore((state) => state.count);

    // Get the handlers (functions to change the state)
    const handlers = counterStore.useHandlers();

    return (
        <div>
            <h1>Count: {count}</h1>
            {/* Set count to a specific number */}
            <button onClick={() => handlers.count.set(10)}>Set to 10</button>
            {/* Increment using current value */}
            <button onClick={() => handlers.count.set((prev) => prev + 1)}>
                Add 1
            </button>
            {/* Reset to initial value (0) */}
            <button onClick={() => handlers.count.reset()}>Reset</button>
        </div>
    );
}
```

That's it! You have a working counter. 🎉

## 📖 Core Concepts

### 1. Creating a Store

A store is where you keep your app's data. It's like a box that holds all your values.

```tsx
const myStore = createStore({
    states: {
        // Put all your data here
        userName: "John",
        age: 25,
        isLoggedIn: false,
    },
});
```

### 2. Reading Data from the Store

Use `useStore` to read data in your components:

```tsx
function MyComponent() {
    // Method 1: Get one value
    const userName = myStore.useStore((state) => state.userName);

    // Method 2: Get multiple values
    const { userName, age } = myStore.useStore((state) => ({
        userName: state.userName,
        age: state.age,
    }));

    return (
        <div>
            Hello, {userName}! You are {age} years old.
        </div>
    );
}
```

### 3. Changing Data (Using Handlers)

Handlers are functions that change your data. The library creates them automatically!

```tsx
function MyComponent() {
    const handlers = myStore.useHandlers();

    // Change the userName
    handlers.userName.set("Jane");

    // Reset to initial value
    handlers.userName.reset();
}
```

## 🎨 Auto-Generated Handlers

The best part? You get **FREE handlers** based on your data type!

### For Simple Values (String, Number)

```tsx
const store = createStore({
    states: {
        name: "John",
        age: 25,
    },
});

const handlers = store.useHandlers();

// ✅ Set to a new value
handlers.name.set("Jane");
handlers.age.set(26);

// ✅ Set using current value
handlers.age.set((currentAge) => currentAge + 1);

// ✅ Reset to initial value
handlers.name.reset(); // Back to "John"
handlers.age.reset(); // Back to 25
```

### For Boolean (True/False)

```tsx
const store = createStore({
    states: {
        isOpen: false,
        isDarkMode: true,
    },
});

const handlers = store.useHandlers();

// ✅ Toggle (switch between true/false)
handlers.isOpen.toggle();

// ✅ Set to specific value
handlers.isDarkMode.set(false);

// ✅ Reset to initial value
handlers.isOpen.reset();
```

### For Arrays (Lists)

```tsx
const store = createStore({
    states: {
        fruits: ["apple", "banana"],
        numbers: [1, 2, 3],
    },
});

const handlers = store.useHandlers();

// ✅ Add to end
handlers.fruits.push("orange");
// Result: ["apple", "banana", "orange"]

// ✅ Add to beginning
handlers.fruits.unShift("mango");
// Result: ["mango", "apple", "banana", "orange"]

// ✅ Remove from end
handlers.fruits.pop();
// Result: ["mango", "apple", "banana"]

// ✅ Remove from beginning
handlers.fruits.shift();
// Result: ["apple", "banana"]

// ✅ Update item at specific position
handlers.fruits.update(0, "grape");
// Result: ["grape", "banana"]

// ✅ Update item using current value
handlers.numbers.update(1, (current) => current * 2);

// ✅ Remove item at specific position
handlers.fruits.remove(1);
// Result: ["grape"]

// ✅ Set entire array
handlers.fruits.set(["kiwi", "melon"]);

// ✅ Reset to initial value
handlers.fruits.reset();
// Result: ["apple", "banana"]
```

### For Objects

```tsx
const store = createStore({
    states: {
        user: {
            name: "John",
            email: "john@example.com",
            settings: {
                theme: "light",
                notifications: true,
            },
        },
    },
});

const handlers = store.useHandlers();

// ✅ Update single property
handlers.user.update("name", "Jane");

// ✅ Update with current value
handlers.user.update("name", (currentName) => currentName.toUpperCase());

// ✅ Update nested property (use dot notation)
handlers.user.update("settings.theme", "dark");

// ✅ Update multiple properties at once
handlers.user.updateMany({
    name: "Jane",
    email: "jane@example.com",
});

// ✅ Update nested properties
handlers.user.updateMany({
    settings: {
        theme: "dark",
    },
});

// ✅ Set entire object
handlers.user.set({
    name: "Bob",
    email: "bob@example.com",
    settings: { theme: "blue", notifications: false },
});

// ✅ Reset to initial value
handlers.user.reset();
```

## 🔧 Custom Handlers

Sometimes you need custom logic. Create your own handlers!

### Sync Handlers (Instant Changes)

```tsx
const store = createStore({
    states: {
        count: 0,
        firstName: "John",
        lastName: "Doe",
    },

    // Define your custom handlers here
    syncHandlers: {
        // Handler with no parameters
        increment: (state) => {
            state.count = state.count + 1;
        },

        // Handler with parameters
        incrementBy: (state, amount: number) => {
            state.count = state.count + amount;
        },

        // Handler that changes multiple values
        setFullName: (state, first: string, last: string) => {
            state.firstName = first;
            state.lastName = last;
        },
    },
});

// Use them in components
function MyComponent() {
    const handlers = store.useHandlers();

    return (
        <div>
            <button onClick={() => handlers.increment()}>Add 1</button>
            <button onClick={() => handlers.incrementBy(5)}>Add 5</button>
            <button onClick={() => handlers.setFullName("Jane", "Smith")}>
                Change Name
            </button>
        </div>
    );
}
```

### Async Handlers (For API Calls)

Perfect for fetching data from servers!

```tsx
const store = createStore({
    states: {
        user: null,
        loading: false,
        error: null,
    },

    asyncHandlers: {
        // Fetch user from API
        fetchUser: async (state, userId: string) => {
            // Set loading to true
            state.loading = true;
            state.error = null;

            try {
                // Fetch from API
                const response = await fetch(
                    `https://api.example.com/users/${userId}`,
                );
                const data = await response.json();

                // Update state with data
                state.user = data;
            } catch (err) {
                // Handle errors
                state.error = "Failed to fetch user";
            } finally {
                // Set loading to false
                state.loading = false;
            }
        },
    },
});

// Use in component
function UserProfile() {
    const { user, loading } = store.useStore((state) => ({
        user: state.user,
        loading: state.loading,
    }));
    const handlers = store.useHandlers();

    return (
        <div>
            <button onClick={() => handlers.fetchUser("123")}>Load User</button>
            {loading && <p>Loading...</p>}
            {user && <p>Name: {user.name}</p>}
        </div>
    );
}
```

## 🎁 Using Providers (Scoped Stores)

Sometimes you want a store that only works within a specific part of your app. Use `createStoreProvider`!

```tsx
import { createStoreProvider } from "@funtools/store";

// Create a provider
const { Provider, useStore, useHandlers } = createStoreProvider({
    states: {
        theme: "light",
        language: "en",
    },
});

// Wrap part of your app
function App() {
    return (
        <Provider>
            <Header />
            <Content />
        </Provider>
    );
}

// Use in any child component
function Header() {
    const theme = useStore((state) => state.theme);
    const handlers = useHandlers();

    return (
        <button
            onClick={() =>
                handlers.theme.set(theme === "light" ? "dark" : "light")
            }>
            Current theme: {theme}
        </button>
    );
}
```

**The difference:**

- `createStore` = Global (available everywhere)
- `createStoreProvider` = Scoped (only available inside `<Provider>`)

## 💡 Performance Tips

### Only Re-render When Needed

Components only re-render when the data they use changes:

```tsx
// ❌ BAD: Component re-renders on ANY state change
const allState = store.useStore((state) => state);

// ✅ GOOD: Component only re-renders when count changes
const count = store.useStore((state) => state.count);

// ✅ GOOD: Component only re-renders when name or age change
const { name, age } = store.useStore((state) => ({
    name: state.name,
    age: state.age,
}));
```

## 📚 Real-World Examples

### Example 1: Todo App

```tsx
const todoStore = createStore({
    states: {
        todos: [] as Array<{ id: number; text: string; done: boolean }>,
    },

    syncHandlers: {
        addTodo: (state, text: string) => {
            state.todos.push({
                id: Date.now(),
                text: text,
                done: false,
            });
        },

        toggleTodo: (state, id: number) => {
            const todo = state.todos.find((t) => t.id === id);
            if (todo) {
                todo.done = !todo.done;
            }
        },

        deleteTodo: (state, id: number) => {
            state.todos = state.todos.filter((t) => t.id !== id);
        },
    },
});

function TodoApp() {
    const todos = todoStore.useStore((state) => state.todos);
    const handlers = todoStore.useHandlers();
    const [input, setInput] = React.useState("");

    return (
        <div>
            <input
                value={input}
                onChange={(e) => setInput(e.target.value)}
                placeholder="Add a todo..."
            />
            <button
                onClick={() => {
                    handlers.addTodo(input);
                    setInput("");
                }}>
                Add
            </button>

            {todos.map((todo) => (
                <div key={todo.id}>
                    <input
                        type="checkbox"
                        checked={todo.done}
                        onChange={() => handlers.toggleTodo(todo.id)}
                    />
                    <span
                        style={{
                            textDecoration: todo.done ? "line-through" : "none",
                        }}>
                        {todo.text}
                    </span>
                    <button onClick={() => handlers.deleteTodo(todo.id)}>
                        Delete
                    </button>
                </div>
            ))}
        </div>
    );
}
```

### Example 2: Shopping Cart

```tsx
const cartStore = createStore({
    states: {
        items: [] as Array<{
            id: number;
            name: string;
            price: number;
            quantity: number;
        }>,
        total: 0,
    },

    syncHandlers: {
        addItem: (
            state,
            product: { id: number; name: string; price: number },
        ) => {
            // Check if item already exists
            const existing = state.items.find((item) => item.id === product.id);

            if (existing) {
                // Increase quantity
                existing.quantity++;
            } else {
                // Add new item
                state.items.push({ ...product, quantity: 1 });
            }

            // Update total
            state.total = state.items.reduce(
                (sum, item) => sum + item.price * item.quantity,
                0,
            );
        },

        removeItem: (state, id: number) => {
            state.items = state.items.filter((item) => item.id !== id);
            state.total = state.items.reduce(
                (sum, item) => sum + item.price * item.quantity,
                0,
            );
        },

        clearCart: (state) => {
            state.items = [];
            state.total = 0;
        },
    },
});
```

### Example 3: User Authentication

```tsx
const authStore = createStore({
    states: {
        user: null as { id: string; name: string; email: string } | null,
        isAuthenticated: false,
        isLoading: false,
    },

    asyncHandlers: {
        login: async (state, email: string, password: string) => {
            state.isLoading = true;

            try {
                const response = await fetch("/api/login", {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify({ email, password }),
                });

                const data = await response.json();

                state.user = data.user;
                state.isAuthenticated = true;
            } catch (error) {
                console.error("Login failed:", error);
            } finally {
                state.isLoading = false;
            }
        },

        logout: async (state) => {
            await fetch("/api/logout", { method: "POST" });
            state.user = null;
            state.isAuthenticated = false;
        },
    },
});
```

## 🎓 TypeScript Support

The library works great with TypeScript! You get autocomplete and type safety.

### Defining State Types

```tsx
// Define your state shape
type UserState = {
    name: string;
    age: number;
    email: string;
};

const store = createStore({
    states: {
        count: 0,
        user: {
            name: "John",
            age: 25,
            email: "john@example.com",
        } as UserState,
    },

    syncHandlers: {
        // TypeScript knows the state type!
        updateUser: (state, newUser: UserState) => {
            state.user = newUser;
        },
    },
});

// TypeScript will catch errors
const handlers = store.useHandlers();
handlers.updateUser({
    name: "Jane",
    age: 26,
    // ❌ Error: missing 'email' property
});
```

## ❓ Common Questions

### Q: When should I use a global store vs provider?

**Use Global Store (`createStore`) when:**

- Data is needed across your entire app (like user auth, theme)
- You want simple setup without wrapping components

**Use Provider (`createStoreProvider`) when:**

- Data is only needed in a specific section
- You want better component isolation
- You're building reusable components

### Q: How is this different from useState?

`useState` is great for local component state. Use `@funtools/store` when:

- Multiple components need the same data
- You want to avoid prop drilling
- You need more powerful update functions

### Q: Can I use multiple stores?

Yes! Create as many stores as you need:

```tsx
const userStore = createStore({ states: { user: null } });
const cartStore = createStore({ states: { items: [] } });
const themeStore = createStore({ states: { theme: "light" } });
```

## 🤝 Contributors

This project is open source and welcomes contributions from the community! We appreciate all the developers who have helped make this library better.

### How to Contribute

We welcome contributions of all kinds:

- 🐛 Bug fixes
- ✨ New features
- 📝 Documentation improvements
- 💡 Suggestions and ideas

To contribute:

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

### Our Contributors

Thanks to all the amazing people who have contributed to this project! 🎉

<!-- Contributors list will be automatically updated -->

Want to see your name here? [Start contributing today!](https://github.com/funtools24/funtools-store/contribute)

## 🔗 Links

- [GitHub Repository](https://github.com/funtools24/funtools-store)
- [Report Issues](https://github.com/funtools24/funtools-store/issues)
- [NPM Package](https://www.npmjs.com/package/@funtools/store)

---

**Made with ❤️ for developers who value simplicity by @funtools24**

Happy coding! 🚀