@plumbiu/react-store v0.9.3
@plumbiu/react-store
Less than 1kb glob state management implement
import { createStore } from '@plumbiu/react-store'
const useCountStore = createStore({
count: 15,
inc() {
this.$set({ count: this.count + 1 })
},
})
export default function App() {
const { count, inc } = useCountStore()
// or use selector, it will avoid global subscriptions
// const count = useCountStore('count')
// const inc = useCountStore('inc')
return (
<>
<div>count: {count}</div>
<buttton onClick={inc}>inc</buttton>
</>
)
}
Immer
You can use createImmerStore
api to reduce the nested structures:
const useImmerStore = createImmerStore({
info: {
address: {
country: 'china',
},
age: 18,
},
changeAddress() {
this.$set((draft) => {
draft.info.address.province = 'hangzhou'
})
},
changeAge() {
this.$set((draft) => {
draft.info.age++
})
},
})
Reading/writing state and reacting to changes outside of React Components
Sometimes we need access state outside React Components.
const store = createStore({ name: 'foo' })
// non-reactive fresh state
store.$getState() // { name: 'foo' }
// Updateding state outside component
store.$setState({ name: 'bar' })
store.$getState() // { name: 'bar' }
// Geting the initial state
store.$getInitialState() // { name: 'foo' }
// Updating state will trigger the listener
const unsub = store.$subscribe(console.log)
// Unscribe the listener
unsub()
Use $use
api add plugin
persit
Cache data in localStorage:
import { createStore } from '@plumbiu/react-store'
import { persist } from '@plumbiu/react-store/plugins'
const usePersonStore = createStore({
age: 21,
name: 'foo',
async changeAge(age: number) {
this.$set({ age })
},
changeName() {
this.$set({ name: this.name + '-' })
},
})
// key for localStorage
usePersonStore.$use(persist({ key: 'person', age: 30000 }))
save
This is useful for some scenarios where you need to withdraw, such as withdrawing text in an input box.
import { createStore } from '@plumbiu/react-store'
import { save } from '@plumbiu/react-store/plugins'
import { useEffect } from 'react'
import hotkeys from 'hotkeys-js'
interface Data {
value: string
setValue: (value: string) => void
save: () => void
back: () => void
// Properties starting with $ are considered ThisType
$save: (point: string) => void
$back: (point: string) => void
}
const SOME_POINT = 'some-point'
const useInputStore = createStore<Data>({
value: '',
setValue(value) {
this.$set({ value })
},
save() {
this.$save(SOME_POINT)
},
back() {
this.$back(SOME_POINT)
},
})
useInputStore.$use(save())
function App() {
const data = useInputStore()
const start = useRef(Date.now())
useEffect(() => {
hotkeys('alt+z', data.back)
}, [])
return (
<input
value={data.value}
onBlur={() => {
start.current = Date.now()
data.save()
}}
onChange={(e) => {
const now = Date.now()
if (now - start.current > 200) {
start.current = now
data.save()
}
data.setValue(e.target.value)
}}
/>
)
}
Global Plugin
If you think it is troublesome to use the $use
method to add plugins to createStore
every time, you can create a custom createStore using a factory function.
For example, this save
plugin:
import { createStoreFactory } from '@plumbiu/react-store'
import { save, type SaveThisType } from '@plumbiu/react-store/plugins'
// Generics added in ThisType
const createStore = createStoreFactory<SaveThisType>([save()])
const SOME_POINT = 'some-point'
const useInputStore = createStore({
value: '',
setValue(value: string) {
this.$set({ value })
},
save() {
this.$save(SOME_POINT)
},
back() {
this.$back(SOME_POINT)
},
})
Custom plugin
export interface Plugin<T> {
// init state
setup?: (state: T) => void
// after re-render
afterUpdate?: (prevState: T, nextState: T) => void
}
Simple persist
const store = createStore({
age: '18',
changeName() {
this.$set({ age: this.age + 1 })
},
})
const KEY = 'custom-plugin'
store.$use({
setup(state) {
const localStore = localStore.getItem(KEY)
if (store === null) {
return
}
const data = JSON.parse(localStore)
for (const key in data) {
state[key] = data[key]
}
},
afterUpdate(_, nextState) {
localStorage.setItem(KEY, JSON.stringify(nextState))
},
})
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago