https://github.com/suhaotian/use-one
A simple state management library in react app. State sharing and persist never been so easy :)
https://github.com/suhaotian/use-one
persistence persistent-storage react-hooks reactjs state state-management store typescript typescript-library
Last synced: about 1 year ago
JSON representation
A simple state management library in react app. State sharing and persist never been so easy :)
- Host: GitHub
- URL: https://github.com/suhaotian/use-one
- Owner: suhaotian
- License: mit
- Created: 2020-08-15T15:18:01.000Z (almost 6 years ago)
- Default Branch: main
- Last Pushed: 2024-04-25T07:51:12.000Z (about 2 years ago)
- Last Synced: 2024-04-26T02:55:17.365Z (about 2 years ago)
- Topics: persistence, persistent-storage, react-hooks, reactjs, state, state-management, store, typescript, typescript-library
- Language: TypeScript
- Homepage:
- Size: 1.34 MB
- Stars: 10
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Use-one.js
Use-one.js: A simple state management lib for React.js
[](https://github.com/suhaotian/use-one/actions/workflows/test-release.yml)
[](https://www.npmjs.com/package/use-one)

# Introduction
[`use-one`](/) is a lightweight and simple state management library for React.js.
**Features**
- Simple hook-based API with no complex concepts
- Easy state sharing across components
- Built-in persistence capabilities for stores and hook states
- Minimal size (gzip ~2KB)
- Written in TypeScript with full type safety
# Table of Contents
- [Use-one.js](#use-onejs)
- [Introduction](#introduction)
- [Table of Contents](#table-of-contents)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Example](#basic-example)
- [Using Immer](#using-immer)
- [Persisting Store State](#persisting-store-state)
- [Persistence in SSR Applications](#persistence-in-ssr-applications)
- [Persisting Hook State](#persisting-hook-state)
- [Advanced TypeScript Usage](#advanced-typescript-usage)
- [API Reference](#api-reference)
- [`create(initialState, options?)`](#createtypeinitialstate-options)
- [Code Generation](#code-generation)
## Installation
**npm**
```bash
npm install use-one --save
```
**pnpm**
```bash
pnpm install use-one
```
## Usage
### Basic Example
```ts
// stores/count.ts
import { create } from 'use-one';
const initialState = { count: 0 };
export const [use, store] = create(initialState);
const actions = {
get state() {
return store.getState();
},
increment() {
store.setState({ count: this.state.count + 1 });
},
decrement() {
store.setState({ count: this.state.count - 1 });
},
};
export const useCount = use;
export const countStore = Object.assign(actions, store);
```
**Using the Store**
```tsx
// CountExample.tsx
import * as React from 'react';
import { useCount, countStore } from './stores/count';
const Counter = () => {
const [{ count }] = useCount();
return (
+1
{count}
-1
{
setTimeout(() => {
countStore.setState({
count: countStore.state.count + 2,
});
}, 2000);
}}
>
async +2
);
};
const ShowCount = () => {
const [state] = useCount();
return Count: {state.count};
};
export default function App() {
return (
<>
>
);
}
```
### Using Immer
Integrate with Immer for immutable state updates:
```ts
// stores/count.ts
import { create } from 'use-one';
import { produce } from 'immer';
const initialState = { count: 0 };
const [use, store] = create(initialState);
const computed = {
get state() {
return store.getState();
},
};
const actions = {
produce(cb: (state: typeof initialState) => void) {
store.setState(produce(cb));
},
increment() {
this.produce((state) => {
state.count++;
});
},
decrement() {
this.produce((state) => {
state.count--;
});
},
};
export const useCount = use;
export const countStore = Object.assign(actions, computed, store);
```
### Persisting Store State
For React Native or Expo applications, install `@react-native-async-storage/async-storage` first.
```ts
import { create, persistStore, wrapState, isClient } from 'use-one';
const initialState = wrapState({ count: 0 }); // Adds ready: false
const [use, store] = create(initialState);
if (isClient) {
persistStore(store, {
key: '@CACHE_KEY',
debounce: 100, // Optional, defaults to 100ms
transform: (state) => state, // Optional state transformer
});
}
const actions = {
get state() {
return store.getState();
},
increment() {
store.setState({ count: this.state.count + 1 });
},
decrement() {
store.setState({ count: this.state.count - 1 });
},
};
export const useCount = use;
export const countStore = Object.assign(actions, store);
```
### Persistence in SSR Applications
To prevent hydration errors in SSR applications (Next.js, Remix, etc.):
```ts
// store.ts
import { create, persistStore, wrapState, onPersistReady } from 'use-one';
const initialState = wrapState({ count: 0 });
const [use, store] = create(initialState);
onPersistReady(() => {
persistStore(store, {
key: '@CACHE_KEY',
debounce: 100,
transform: (state) => state,
});
});
const actions = {
get state() {
return store.getState();
},
increment() {
store.setState({ count: this.state.count + 1 });
},
decrement() {
store.setState({ count: this.state.count - 1 });
},
};
export const useCount = use;
export const countStore = Object.assign(actions, store);
```
```tsx
// layout.tsx
import { Provider as PersistProvider } from 'use-one';
export default function Layout({ children }: { children: React.ReactNode }) {
return {children};
}
```
### Persisting Hook State
Persist any hook's state independently of stores:
```tsx
import { useState } from 'react';
import { usePersist } from 'use-one';
export function Counter() {
const [count, setCount] = useState(0);
const [isReady, clean] = usePersist({
key: '@count-store-key',
getState: () => count,
setState: setCount,
});
if (!isReady) return
Loading...;
return (
{count}
setCount(count + 1)}>+1
setCount(count - 1)}>-1
Clear Cache
);
}
```
### Advanced TypeScript Usage
Prevent property conflicts using `StrictPropertyCheck`:
```ts
import { create, type StrictPropertyCheck } from 'use-one';
const initialState = { count: 0 };
const [use, store] = create(initialState);
const _actions = {
get state() {
return store.getState();
},
increment() {
store.setState({ count: this.state.count + 1 });
},
decrement() {
store.setState({ count: this.state.count - 1 });
},
};
const actions: StrictPropertyCheck = _actions;
export const useCount = use;
export const countStore = Object.assign(actions, store);
```
## API Reference
### `create(initialState, options?)`
Creates a new store with the following options:
- `useEffect`: Boolean (default: true) - Uses `useEffect` when true, `useLayoutEffect` when false
- `name`: String - Optional name for the store
Returns `[useHook, store]` where `store` provides:
- `getState()`: Get current state
- `setState(newState)`: Update state
- `forceUpdate()`: Force component updates
- `subscribe(cb: (state) => void)`: Subscribe to state changes
- `syncState(newState)`: Update state without triggering updates
- `destroy()`: Clean up store resources
## Code Generation
Visit [use-one-templates](https://github.com/suhaotian/use-one-templates) for boilerplate code generation tools, especially useful for managing multiple stores in larger applications.