
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.
Simple class based hook and state manager for React.
class CounterKore extends Kore {
add = () => this.setState( s => s + 1 );
subtract = () => this.setState( s => s - 1 );
}
function Counter() {
const [count, {add, subtract}] = useKore(CounterKore, 0);
return (
<div>
<span>{count}</span>
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
</div>
);
}
KeyPoints:
This readme looks better in gitHub
This package is similar to use-Fun
npm add sokore
function useKore( koreClass, iniVal? )
returns [ state, kore ];
This is a simple, classic-behavior custom hook that:
import { Kore, useKore } from "SoKore";
class CounterKore extends Kore<number> {
state = 0;
public add = () => this.setState( s => s + 1 );
public subtract = () => this.setState( s => s - 1 );
public reset = () => this.setState( 0 );
}
function Counter() {
const [count, {add, subtract}] = useKore(CounterKore);
return (
<div>
<span>{count}</span>
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
</div>
);
}
function useKore.should( koreClass, ( prev, next ) => boolean , iniVal? )
returns [ state, kore ];
You can use the function property should, that add a compare function parameter to the useKore hook.
The component will trigger re-renders only if this function returns true. Parameters are previous state and next state.
// re-renders only if counter is pair
function Counter() {
const [count, {add, subtract}]
= useKore.should(CounterKore, (_, n) => n % 2 == 0 );
return (
<div>
<span>{count}</span>
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
</div>
);
}
The useSoKore hook and the getSoKore utility method, create, use, store, and share a unique instance of your kore class across the application, at global scope. Either can update the state between components, but getSoKore is not a hook, so never trigger a re-render in the component.
To bind the components using the useSoKore hook and/or the getSoKore method together, just use the same kore class.
function useSoKore( koreClass, iniVal? )
returns [ state, kore ];
This hook is equal to useKore, but store, or use an already stored, instance of your kore class and its state.
import { Kore, useSoKore } from "SoKore";
class CounterKore extends Kore<number> {
state = 0;
public add = () => this.setState( s => s + 1 );
public subtract = () => this.setState( s => s - 1 );
public reset = () => this.setState( 0 );
}
function Counter() {
const [count, {add, subtract}] = useSoKore(CounterKore);
return (
<div>
<span>{count}</span>
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
</div>
);
}
function getSoKore( koreClass )
returns kore;
Get the instance of your kore using the getSoKore utility method. This method is not a hook, so it never triggers a new render.
You can use this method mainly for two things:
class CounterKore extends Kore<number> {
state = 0;
public add = () => this.setState( s => s + 1 );
public subtract = () => this.setState( s => s - 1 );
public reset = () => this.setState( 0 );
}
function Controls() {
const {add, subtract} = getSoKore(CounterKore);
return (
<div className="buttons">
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
</div>
);
}
function Counter() {
const [count] = useSoKore(CounterKore);
return (
<div>
<span>{count}</span>
</div>
);
}
export function App() {
return (
<div>
<Controls />
<Counter />
</div>
);
}
function useSoKore.select( koreClass, s => f(s), iniVal? )
returns [ f(s), kore ];
This function property adds a "selector" function parameter to the useSoKore hook. This will perform a shallow comparison for the selector results with the prev and next states and will trigger a re-render only if these results are different.
The selector must be a function that takes the state and transforms it in an array, an object, or a value. The result type must remain stable, except for undefined. The hook will return the selector result as first element.
Use only if you have performance problems; this hook avoids some unnecessary re-renders but introduces a dependency array of comparisons. Always prefer useSoKore( koreClass ) no selector and the getSoKore method first.
function useSoKore.should( koreClass, ( prev, next ) => boolean , iniVal? )
returns [ state, kore ]
You can use the function property should, that add a compare function parameter to the useSoKore hook. Parameters are previous state and next state.
The component will trigger re-renders only if this function returns true
function useSoKore.selectShould(kClass, s => f(s), (p, n) => bool, iniVal?)
returns [ f(s), kore ];
You can use the function property selector and should together, that adds a selector and a compare function parameter to the useSoKore hook.
In this case the component will trigger re-renders only if the compare function returns true, regardless of the selector function.
If you don't pass a compare function, it defaults to true, meaning always trigger re-render for any part of the state changed, with the selector is applied. This can result in better performance than using select or should function alone.
class CounterKore extends Kore<{chairs:number, tables:number, rooms:number}> {
state = {
chairs: 0,
tables : 0,
rooms : 10
}
_koreConfig = { merge : true }
addChairs = () => this.setState( c =>( { chairs: c.chairs + 1 }) );
subtractChairs = () => this.setState( c => ({chairs : c.chairs - 1}) );
addTables = () => this.setState( t => ({tables: t.tables + 1}) );
subtractTables = () => this.setState( t => ({tables: t.tables - 1}) );
setRooms = (n:number) => this.setState( {rooms : n} ),
}
function Chairs() {
// This component re-renders only if the compare function(prevState, nextState) returns true
const [{chairs},{addChairs,subtractChairs}]
= useSoKore.should( CounterKore, (p, n) => p.chairs !== n.chairs );
return <>
<span>Chairs: {chairs}</span>
<button onClick={addChairs}>+</button>
<button onClick={subtractChairs}>-</button>
</>
}
function Tables() {
// This component re-renders only if tables.toString() changes
// Here tables is a string
const [tables, {addTables, subtractTables}]
= useSoKore.select( CounterKore, s => s.tables.toString() );
return <>
<span>Tables: {tables}</span>
<button onClick={addTables}>+</button>
<button onClick={subtractTables}>-</button>
</>
}
function getSoKore( koreClass, ( koreUpdatedInstance ) => any )
returns unsubscribe();
You can use the getsokore method to suscribe a function to state changes instead of get the instance directly.
This example shows how to suscribe and unsubscribe a function that logs counter changes :
useEffect( () => getSoKore(SimpleCounterKore, s => console.log("Counter: ", s.state) ) , [] )
The kore object is an instance of a class you wrote with actions and that extends the abstract Kore class of this package. Extending the Kore class gives to the child a state property and a setState method, among others.
This instance is created by the hook with the kore class you wrote that is passed as argument.
class CounterKore extends Kore<{chairs:number, tables:number, rooms:number}> {
state = { chairs: 0, tables : 0, rooms : 10 }
...
// OR
function Counter() {
const [counters] = useSoKore(CounterKore, { chairs: 0, tables : 0, rooms : 10 });
...
You can set an initial state in the class definition or pass an initial value on the hook. You should not initialize the state with both methods, but if you do, the initial value on the hook has priority.
Prefer setting the state in the class definition for easier readability.
Code you wrote in instanceCreated() method will update the initial state.
class CounterKore extends Kore<{chairs:number, tables:number, rooms:number}> {
state = {
chairs: 0,
tables : 0,
rooms : 0
}
instanceCreated = () => {
fetch('https://myapi.com/counters')
.then( r => r.json() ).then( r => this.setState(r) );
}
}
Optional method that is called only once when an instance is created. If exists in the instance, this method is called by the useSoKore or useKore hook the first time a component in the application using the hook is effectively mounted and when the instance is "newly created".
This method has NOT the same behavior as mount callback of a component in React when using useSoKore. The only way this method is called again by the hook is by destroying the instance first with destroyInstance().
kore.destroyInstance(force?: boolean) => void
You may destroy the stored instance when needed using the destroyInstance(force?) method. This method should be called on the unmount callback of the component using it, or before loading a new component.
This method first checks if there are active state hook listeners active. If there isn't, the instance reference is deleted, and the instanceDeleted() method is called if exists. If force parameter is true, deletes the instance without checking anything (force destroy with caution).
If you implement instanceDeleted(), remember that it is not the equivalent of an unmount component callback.
export function App() {
const [ {data}, {load, destroyInstance} ] = useSoKore( CounterKore );
useEffect( () => {
load();
return () => destroyInstance(); // instanceDeleted() would be called
}, [] );
...
}
//default:
_koreConfig = { merge : false, destroyOnUnmount : false }
You may configure your kore object by setting the optional property _koreConfig in your kore class. It has two boolean options:
class CounterKore extends Kore<{chairs:number, tables:number, rooms:number}> {
state = {
chairs: 0,
tables : 0,
rooms : 10
}
_koreConfig = { merge : true }
addChairs = () => this.setState( c => ( { chairs: c.chairs + 1 }) );
subtractChairs = () => this.setState( c => ({chairs : c.chairs - 1}) );
addTables = () => this.setState( t => ({ tables: t.tables + 1 }) );
subtractTables = () => this.setState( t => ({tables: t.tables - 1}) );
resetAll = () => this.setState( { chairs: 0, tables : 0 } );
}
function Chairs() {
const [{chairs},{addChairs, subtractChairs}] = useSoKore(CounterKore);
return <>
<span>Chairs: {chairs}</span>
<button onClick={addChairs}>+</button>
<button onClick={subtractChairs}>-</button>
</>
}
function Tables() {
const [{tables},{addTables, subtractTables}] = useSoKore(CounterKore);
return <>
<span>Tables: {tables}</span>
<button onClick={addTables}>+</button>
<button onClick={subtractTables}>-</button>
</>
}
Replacing the state is the default mode for a kore object setState, but you can configure your kore setState to merge it. This can be useful to refactor old class components.
Note that the useSoKore hook will trigger re-render for any part of the state changed. In the example above, Tables component will re-render if the chairs value is changed. This behavior can be optimized using select or should "subHooks"
Merging mode is only for an object-like state, and there is no check of any kind for this before doing it, so its on you to guarantee an initial and always state object.
Tries to delete the instance in each unmount of each component. Is successfully deleted if there are no active listeners (other components using it).
This can be useful if you need to "restart" your components on unmount that are using a stored kore object and it is not clear which related component will unmount last.
with destroyOnUnmount enabled, SoKore will behave similarly to a context, with fresh instances on remount components.
The instance creation is managed by the hook, so you can't create new instances from your class. One way to use your class again with this hook without duplicating code is to extend it:
class BetaCounterKore extends CounterKore {};
Extending a generic class with Kore lets you encapsulate common functionality:
MyGenericApiKore.ts : A generic kore for my API
type ApiData = {
data?: Record<string, any>[];
isLoading: boolean;
}
export abstract class MyGenericApiKore extends Kore<ApiData>{
state : ApiData = {
data: undefined,
isLoading: false
}
protected _koreConfig = { merge: true };
abstract readonly loadUri : string; // making loadUri property obligatory to define in inherited class
readonly saveUri? : string = undefined;
public load = ( params? : string ) => {
this.setState({isLoading: true});
return fetch( this.loadUri + ( params ?? '' ) )
.then( r => r.json() )
.then( resp => this.setState({ data : resp?.data ?? [] , isLoading: false}) )
}
public modify = ( item : Record<string, any>, changes : Record<string, any> ) =>
this.setState( s => ({ data : s.data?.map( i => i === item ? { ...i, ...changes } : i ) }) )
public delete = ( item : Record<string, any> ) =>
this.setState( s => ({ data : s.data?.filter( i => i !== item ) )} )
public append = ( item : Record<string, any> ) =>
this.setState( s => ({ data : s.data?.concat( item ) }) )
public save = ( params? : Record<string, any> ) => {
this.setState({isLoading: true});
return fetch( this.saveUri ?? '', { method: 'POST', body : JSON.stringify(params)} )
.then( r => r.json() )
.then( () => this.setState({ isLoading: false }) )
}
}
MyComponent.tsx
import { MyGenericApiKore } from "./MyApiHandler";
class SpecificApiKore extends MyGenericApiKore {
loadUri = 'https://myapi/specific'
}
export function MyComponent() {
const [{data, isLoading}, {load, formModify, save} ]
= useSoKore( SpecificApiKore );
useEffect( () => { load() }, [] );
return ( ... );
}
If you work with some framework data loader, like Remix loaders, you can use the getSoKore method to preload data, then useSoKore as usual in your component. But it is important to not set an initial state; otherwise, this initial state will override loader data.
export async function loader() {
await getSoKore( koreClass ).load();
return;
}
export default function App() {
// state already have data here:
const [ {data} ] = useSoKore( koreClass );
return (...)
}
setState() is just a wrapper for the actual _setState() function that comes in your kore object. In Javascript you can directly modify it; in Typescript you need to define the setState type as a second generic type of your kore class.
import { produce, WritableDraft } from "immer";
type CountState = {chairs:number, tables:number, rooms:number};
type MySetStateType = ( recipe : (draft: WritableDraft<CountState>) => void ) => void;
export class CounterKore extends Kore<CountState, MySetStateType> {
state = {
chairs: 0,
tables : 0,
rooms : 10
}
public setState : MySetStateType = ( recipe ) => this._setState( s => produce(s, recipe) )
}
function Chairs() {
const [{chairs}, {setState}] = useSoKore(CounterKore);
return <>
<span>Chairs: {chairs}</span>
<button onClick={() => setState( s => { s.chairs++ } )}>+</button>
<button onClick={() => setState( s => { s.chairs-- } )}>-</button>
</>
}
function Tables() {
const [{tables}, {setState}] = useSoKore(CounterKore);
return <>
<span>Tables: {tables}</span>
<button onClick={() => setState( s => { s.tables++ } )}>+</button>
<button onClick={() => setState( s => { s.tables-- } )}>-</button>
</>
}
public setState = this._setState
You may define a constructor in your kore class. But is not necessary
Prefer defining an instanceCreated() method on the kore over the constructor to execute initial code.
constructor( initialState? : T ) {
super(initialState);
//your code
}
Constructor code constructors are not part of the mounting/unmounting logic of React. Hook state listeners may or may not be ready when the code executes.
It is safe to write code that does not involve changing the state directly.
FAQs
Simple object based hook and state manager for React.
The npm package sokore receives a total of 0 weekly downloads. As such, sokore popularity was classified as not popular.
We found that sokore 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.