1.0.3 • Published 2 years ago

my-remind v1.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

A small, fast and scalable state-management solution using simplified flux principles. Has a comfy api based on hooks, isn't boilerplatey or opinionated.

npm install my-remind # or yarn add my-remind

First create a store

Your store is a hook! You can put anything in it: primitives, objects, functions. The set function merges state.

import remind from 'my-remind'

const { useRemind } = remind((set) => ({
  mindLevel: 0,
  increase: () => set((mind) => ({ mindLevel: mind.mindLevel + 1 })),
  reset: () => set({ mindLevel: 0 }),
}))

// or

const { useRemind, setMind } = remind({
  mindLevel: 0,
})

const increase = () => {
  setMind((mind) => ({ mindLevel: mind.mindLevel + 1 }))
}

const reset = () => {
  setMind({ mindLevel: 0 })
}

Then bind your components, and that's it!

Use the hook anywhere, no providers needed. Select your state and the component will re-render on changes.

function CounterDisplay() {
  const { mind } = useRemind((mind) => mind.mindLevel)
  return <h1>mind level: {mind.mindLevel}</h1>
}

function Controls() {
  const { mind } = useRemind((mind) => mind.increase)
  return <button onClick={mind.increase}>one up</button>
}

Why remind over redux?

  • Simple and un-opinionated
  • Makes hooks the primary means of consuming state
  • Doesn't wrap your app in context providers

Why remind over context?

  • Less boilerplate
  • Renders components only on changes
  • Centralized, action-based state management

Recipes

Fetching everything

You can, but mind that it will cause the component to update on every state change!

const { mind } = useRemind()

// or

const [mind] = useRemind()

You can pick your mind from array or plain object.

Selecting multiple state slices

It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks.

const { mind } = useRemind((mind) => mind.mindLevel)

For more control over re-rendering, you may provide any custom equality function.

const { mind } = useRemind((state) => state.mindLevel, {
  equality: (oldLevel, newLevel) => compare(oldLevel, newLevel),
})

Overwriting state

The set function has a second argument, false by default. Instead of merging, it will replace the state model. Be careful not to wipe out parts you rely on, like actions.

import omit from 'lodash-es/omit'

const { useRemind } = remind((set) => ({
  name: 'mind',
  level: 2,
  deleteEverything: () => set({}, true), // clears the entire store, actions included
  deleteName: () => set((state) => omit(state, ['name']), true),
}))

Async actions

Just call set when you're ready, remind doesn't care if your actions are async or not.

const { useRemind } = remind((set) => ({
  ideas: {},
  fetch: async (path) => {
    const response = await fetch(path)
    set({ ideas: await response.json() })
  },
}))

Remind generate you status for each async action.

const MyComponent = () => {
  const { mind } = useRemind()
  const [fetch, status] = mind.fetch

  return <button>{status === 'idle' ? 'fetch' : status}</button>
}

Read from state in actions

set allows fn-updates set(state => result), but you still have access to state outside of it through get.

const { useRemind } = remind((set, get) => ({
  name: "mind",
  action: () => {
    const sound = get().name
    // ...
  }
})

Reading/writing state and reacting to changes outside of components

Sometimes you need to access state in a non-reactive way, or act upon the store. For these cases the resulting hook has utility functions attached to its prototype.

const head = remind(() => ({ mindLevel: 0 }))

// Getting non-reactive fresh state
const mindLevel = head.mind.mindLevel
// Listening to all changes, fires synchronously on every change
const unsub1 = head.subscribe(console.log)
// Updating state, will trigger listeners
head.setMind({ mindLevel: 2 })
// Unsubscribe listeners
unsub1.unsubscribe()
// Destroying the store (removing all listeners)
head.destroy()

// You can of course use the hook as you always would
function Component() {
  const { mind } = useRemind(mind => state.mindLevel)

Using subscribe with selector

If you need to subscribe with selector, subscribeWithSelector middleware will help.

With this middleware subscribe accepts an additional signature:

subscribe(listener, selector?, equality?): { unsubscribe, body }

Can't live without redux-like reducers and action types?

const types = { increase: 'INCREASE', decrease: 'DECREASE' }

const reducer = (state, { type, by = 1 }) => {
  switch (type) {
    case types.increase:
      return { mindLevel: state.mindLevel + by }
    case types.decrease:
      return { mindLevel: state.mindLevel - by }
  }
}

const { useRemind } = remind((set) => ({
  mindLevel: 0,
  dispatch: (args) => set((mind) => reducer(mind, args)),
}))

const { mind } = useRemind((mind) => mind.dispatch)
mind.dispatch({ type: types.increase, by: 2 })
1.0.3

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago