0.9.3 • Published 11 months ago

@plumbiu/react-store v0.9.3

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

@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))
  },
})
0.9.3

11 months ago

0.9.2

11 months ago

0.9.0

11 months ago

0.9.1

11 months ago

0.7.5

11 months ago

0.7.4

11 months ago

0.7.2

12 months ago

0.7.1

12 months ago

0.6.2

12 months ago

0.6.1

12 months ago

0.6.0

12 months ago

0.5.2

12 months ago

0.5.1

12 months ago

0.4.1

12 months ago

0.4.0

12 months ago

0.3.0

12 months ago

0.2.2

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.0

1 year ago

0.0.4

1 year ago

0.0.3

1 year ago

0.0.2

1 year ago

0.0.1

1 year ago