
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@bearbytes/karma
Advanced tools
This is a library in the Redux family of state management solutions, featuring a different set of tradeoffs. It is meant to be used with React (or React Native).
get, subscribe, updateProbably not. For anything serious, stick to one of the more established solutions.
I'm dogfooding it for my own projects, but there are probably still bugs to be fixed.
Install the dependency:
npm i @bearbytes/karma
Create some type definitions for your state:
interface ITodo {
id: string
title: string
isDone?: boolean
}
interface IAppState {
todos: ITodo[]
}
Create an initial state object:
const initialAppState: IAppState = { todos: [] }
Call createStore, which also creates typesafe hooks and a Context container. You often don't need everything that function returns, just take out what you need.
import { createStore } from '@bearbytes/karma'
const {
// Global store instance. Most apps only need this one.
store,
// If you want to have multiple store instances in the app,
// this Container can be put into the React component hierarchy
// to create a new store context below it.
Container,
// Get access to the store from the nearest Container,
// or the global instance if no Container is used.
useStore,
// This is the most important hook, which extracts data from the
// store and updates the component whenever that data changes.
useStoreState,
// This hook can be used in a React component to create a callback to
// update state. It has some advantages to calling store.update() directly.
useStoreUpdate,
} = createStore(initialState)
// In most cases, only a single store is needed:
export { store, useStoreState, useStoreUpdate }
Get the current value saved in the store:
const appState = store.get()
Subscribe to the current and all future values, using rxjs:
store.subscribe((s) => {
console.log('AppState is now:', s)
})
Register a listener to a specific piece of data in a React component:
function TodoListComponent() {
// this component will be re-rendered when `todos` are updated,
// but not when a different part of the store is updated
const todos = useStoreState((s) => s.todos)
return (
<>
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} />
))}
</>
)
}
Instead of dispatching an action at one place and having code to update the state in another place, we simply inline this code:
store.update((s) => {
s.todos.push({ id: 123, title: 'Stop writing stupid Todo apps' })
})
You are only allowed to mutate state within the update method of the store. Subscribers to the store will only see the updated state after the function exits. Karma uses immer under the hood, so the same rules apply (state must consist of plain objects and arrays without circular references).
If you use multiple stores in your application, make sure to get the correct one from a React component:
function AddTodoButton(props: { todo: ITodo }) {
const store = useStore()
function onPress() {
store.update((s) => {
s.todos.push(props.todo)
})
}
return <button onPress={onPress}>Add</button>
}
A better way might be to use the useStoreUpdate hook, which will automatically wrap the update function in useCallback, which can avoid re-renders when passed down to child components:
function AddTodoButton(props: { todo: ITodo }) {
const onPress = useStoreUpdate(
// Updates done to the store when onPress is called
(s) => {
s.todos.push(props.todo)
},
// DependencyList passed to useCallback
//all variables used in the update function should be put here
[props.todo]
)
return <button onPress={onPress}>Add</button>
}
Dealing with asynchronicity is often an issue with Redux-like solutions. Usually, you have to think about whether to use Thunks or Sagas or something completely different.
With Karma, there is just the update function, which may never be async. Don't overthink it, just update the state synchronously whenever something relevant happens:
async function downloadMultipleFiles(urls: string[]) {
// Set the loading state
store.update((s) => {
s.downloadInProgress = true
})
// Download files in parallel
await Promise.all(urls.map(downloadSingleFile))
// All downloading done
store.update((s) => {
s.downloadInProgress = false
})
}
async function downloadSingleFile(url: string) {
const data = await fetch(url)
await storeFileOnDisk(url, data)
// Update the state after each file done
store.update((s) => {
s.filesDownloaded.push(url)
})
}
Should work out of the box. Note that since our "actions" are just anonymous lambda functions, all of them will be called "Anonymous Action".
FAQs
State management for React, comparable but different to Redux
We found that @bearbytes/karma demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.