npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-simple-reducer

v1.1.0

Published

Local state manager that enhances react's `useReducer`. Heavily inspired by [Redux Toolkit](https://redux-toolkit.js.org/).\ Leverage Typescript to make **everything** type safe, from the creation of the store to it's usage.\ The same way as redux toolkit

Downloads

43

Readme

React Simple Reducer

Local state manager that enhances react's useReducer. Heavily inspired by Redux Toolkit.
Leverage Typescript to make everything type safe, from the creation of the store to it's usage.
The same way as redux toolkit, uses immer to reduce the state.

How to use

Create a store using createSimpleStore passing the state and reducers. Optionally you can pass thunks and an options object.

const TodosStore = createSimpleStore({
  todo: null as TodoType | null,
  todos: [] as TodoType[],
}, {
  updateTodo (state, todo: TodoType) {
    state.todo = todo
  },
  addTodo (state) {
    state.todos.push({ todo: state.todo, saved: false })
    state.todo = ''
  },
  saveTodosSuccess (state) {
    state.todos.forEach(t => t.saved = true)
  },
}, {
  thunks: {
    saveTodos ({ saveAll = true }) {
      return async (dispatch, getState) => {
        const todos = getState().todos
        await api.save(todos)
        dispatch(TodosStore.actions.saveTodosSuccess())
      }
    }
  },
  options: {
    cache: {
      key: 'TODOS_STORE',
      local: 'SESSIONSTORAGE'
    }
  }
})

Use the Provider to make the store available for every children component.
In the first component, outside of the Provider, you don't have access the the store and dispatch. That helps you mantain your entry component clean and declarative.

const TodoComponent = () => {
  return (
    <TodosStore.Provider init={initFn}>
      <NewTodo />
      <TodoList />
      <SaveTodos />
    </TodosStore.Provider>
  )
}

Any child component will have access to the store.
useState and useDispatch are custom hooks that internally use useContext to provide with the current state and dispatch function respectively.
You can dispatch an action reducer or a thunk (declared optionally in the options object, passed as createSimpleStore's third param)
There is a helper called actions, which holds all the reducer function and return the action object and a helper called thunks, which hold the thunks themselves. dispatch will call the thunk enhancing it with dispatch itself and getState (which will get the current state, even if it changes during an async call). Both actions and thunks will correctly type their params.

const ChildComponent = () => {
  // get anything from the state using useState()
  const { todo, todos } = TodosStore.useState()

  // get the dispatch function
  const dispatch = TodosStore.useDispatch()

  // dispatch actions using two methods: object and actions
  dispatch({ type: 'updateTodo', payload: { text: 'NEW TODO TEXT' } })
  dispatch(TodosStore.actions.updateTodo({ text: 'NEW TODO TEXT' }))

  // dispatch async actions using thunks
  dispatch(TodosStore.thunks.saveTodos({ saveAll: false }))
}

Aditional functionalities

In the entry component, in which the Provider is declared, you don't have access to state or dispatch. If you need access to the state for some reason (show or hide components, for instance), use the high order component GetState. Usually, there is a startup for the store (load initial data through api, set initial parameters as received by props, for instance). Provider has the init prop, which expects a function with dispatch as first and only param. Through this function, you can initialize the store. A caveat is that this function is observed, and if it changes, it's called again, so if it's called multiple times, it needs to be wrapped on a React.useCallback. That behavior is useful for changes in props, to reflect on the store.

const TodoComponent = ({todoGroupId}) => {
  const initFn = React.useCallback((dispatch: ReturnType<typeof AuthStore.useDispatch>) => {
	  dispatch(AuthStore.thunks.getTodos(todoGroupId))
  }, [todoGroupId])
  return (
    <TodosStore.Provider init={initFn}>
      <NewTodo />
      <TodoList />
      <SaveTodos />
      <TodosStore.GetState>
        {state => (<>Total of Todos: {state.todos.length}</>)}
      </TodosStore.GetState>
    </TodosStore.Provider>
  )
}

There is built-in caching on localStorage and sessionStorage, that you can configure in the options param of the optional object present in the third param of the function.

Redux Devtools

A connection to redux devtools is automatically made, you can debug and time travel out of the box. Not all functionalities are implemented at this time. Open an issue or PR if you need something else.


Using Selectors

You can use createSelector from reselect and the custom hook useSelector.

// you can also get the state type, so your selector will be type safe
type IState = ReturnType<typeof TodosStore.useState>
export const selectUnsavedTodos = createSelector(
    (s: IState) => s.todos,
    (todos) => {
      return todos.filter(t => !t.saved)
    }
)
const ChildComponent = () => {
  const unsavedTodos = TodosStore.useSelector(selectUnsavedTodos)
}

Remember to return an existing value from the input selectors functions (the first functions of the selector)

// WRONG - The object is created at every call, and not memoized.
selectDuplicateTodo: createSelector(
    (s: IState) => ({todos: s.todos, todo: s.todo}),
    ({todos, todo}) => getDuplicates(todos, todo)
)
// CORRECT - Will only evaluate when todos or todo changes
selectDuplicateTodo: createSelector(
    (s: IState) => s.todos,
    (s: IState) => s.todo,
    (todos, todo) => getDuplicates(todos, todo)
)

You can also pass params for the selector by creating a new value and memoizing it using useMemo, before passing to the selector.

selectSpecificTodo: createSelector(
    (s: IState & {id: number}) => s.todos,
    ({id}) => id,
    (todos, id) => todos.find(t => t.id)
)
const ChildComponent = () => {
  const state = TodosStore.useState()
  const [id, setId] = useState(1)
  const stateWithId = useMemo(() => ({...state, id}), [state, id])
  const pacificTodo = selectSpecificTodo(stateWithId)
}