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

redonk

v4.0.6

Published

πŸŽ‰ Ridiculously simple state management in pure React

Downloads

1

Readme

Redonk logo

πŸŽ‰ Ridiculously simple state management in pure React

npm

Example demo with Counter and Todos

Installation

npm install redonk
yarn add redonk

Highlights

  • Pure React state and context
  • Light - around 760B in production
  • Can be used for both global state and simpler cases when you just need to "lift up" your state
  • Solves performance issues of context by creating multiple contexts and passing slices of state to them
  • Full Typescript support

Usage

  1. Create the store
// store.jsx
export const {
  Provider,
  useSliceState,
  useActions,
  useRedonkState,
} = createStore({
  slices: {
    count: 0,
    todos: {
      items: [{ id: '1', text: 'Learn React', isDone: false }],
    },
  },
  reducers: {
    increment: state => {
      return {
        ...state,
        count: state.count + 1,
      };
    },
    decrement: state => {
      return {
        ...state,
        count: state.count - 1,
      };
    },
    addTodo: (state, todo) => {
      return {
        ...state,
        todos: {
          ...state.todos,
          items: [...state.todos.items, todo],
        },
      };
    },
  },
});
  1. Wrap your component tree with the returned Provider
// app.jsx
import { Provider } from './store';
import { Counter } from './Counter';
import { Todos } from './Todos';

const App = () => {
  return (
    <Provider>
      <Counter />
      <Todos />
    </Provider>
  );
};
  1. And just use your state, actions and computed fields !!! πŸ₯³
  // counter.jsx
  import {useSliceState, useActions } from "./store"

  export const Counter = () => {
    const count = useSliceState("count")
    const {increment, decrement} = useActions()

    return (
      <div>
        <button onClick={decrement}>-</button>
        <div>{count}</count>
        <button onClick={increment}>+</button>
      </div>
    )
  }

  // todos.jsx

  import {useSliceState, useActions} from "./store"

  export const Todos = () => {
    const {items: todos} = useSliceState("todos")
    const {addTodo} = useActions()

    return (
      <div>
        <ul>
          {todos.map(todo => (
            <li key={todo.id}>
              {todo.text}
              {todo.isDone && "βœ…"}
            </li>
          ))}
        </ul>
        <button onClick={() =>
          addTodo({
            id: Date.now().toString(),
            text: "New todo",
            isDone: false
          })}>
          Add todo
        </button>
      </div>
    )
  }

API

The library only exports one function!

createStore({ slices, reducers }) => { Provider, useSliceState, useActions, useRedonkState }

import { createStore } from 'redonk';

const { Provider, useSliceState, useActions, useRedonkState } = createStore({
  slices: {
    count: 0,
  },
  reducers: {
    increment: state => {
      return {
        ...state,
        count: state.count + 1,
      };
    },
    decrement: state => {
      return {
        ...state,
        count: state.count - 1,
      };
    },
  },
});

Accepts

slices

An object that defines how the state will be split into contexts.

A slice can be any value (primitive or object)

In this example we will have two state contexts, one for counter and the other for todos:

createStore({
  slices: {
    counter: {
      count: 0,
    },
    todos: {
      items: [],
      filter: 'all',
    },
  },
});

reducers

An object with your case reducers. A case reducer is a function that accepts two arguments: state and payload and should return the new state. For each of the case reducers a corresponding action will be created with the same name.

Returns

Provider

Holds the state, actions and renders all of the contexts. You need to wrap your component tree with it:

return (
  <Provider>
    {...}
  </Provider>
)

useSliceState(sliceKey: string)

Returns the state of the slice whose key you passed in. Only causes a render if you update that slice.

const counterState = useSliceState('counter');

useActions()

Returns all of the actions. Never causes a render because actions are memoized.

const actions = useActions();

useRedonkState: () => State

Hook for getting the entire state of Redonk. Causes a render on every state update, so use wisely!

const entireState = useRedonkState();

Usage with Typescript

When creating the store, you should define the type of your state:

import { createStore } from 'redonk';

type Todo = {
  id: string;
  text: string;
  isDone: boolean;
};

type AppState = {
  counter: {
    count: number;
  };
  todos: {
    items: Todo[];
  };
};

// like this
createStore({
  slices: {
    counter: {
      count: 0,
    },
    todos: {
      items: [],
    },
  } as AppState,
});

// or like this
const slices: AppState = {
  counter: {
    count: 0,
  },
  todos: {
    items: [],
  },
};

createStore({
  slices,
});

You also need to define the type of the payload for each case reducer:

createStore({
  slices: {
    counter: {
      count: 0,
    },
  },
  reducers: {
    // you can name the payload argument anything you want
    incrementByAmount: (state, payload: number) => {
      return {
        ...state,
        counter: {
          ...state.counter,
          count: state.counter.count + payload,
        },
      };
    },
  },
});

And voila! You have intellisense everywhere:

// inside component
const counterState = useSliceState('counter'); // counter state is correctly inferred as { count: number }

const actions = useActions(); // actions are correctly inferred including the type of Payload

const entireState = useRedonkState(); // correctly inferred as AppState