
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 hook and state manager for React using [Fun]ctions.
const counter = ( count = 0 ) => ({
state : () => count,
add: () => count++,
subtract: () => count--
})
function Counter() {
const [count, {add, subtract}] = useFun( counter );
return <>
<span>{count}</span>
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
</>
}
KeyPoints:
This readme looks better in gitHub
This package is similar to SoKore
useFun( initFun, select? )
returns [ state, funCollection ]
initFun : A Fun collection, a function that returns a Fun collection or a Class that construct a Fun collection.
select : Optional parameter. a Function that accepts the "stateDefinition" and returns a derived state.
state : The "stateDefinition" with updated values.
funCollection : The Fun collection defined by initFun. A Fun collection is an object that have actions and a function named state that returns a "stateDefinition".
npm add use-fun
const counterLog = ( ) => {
let count = 0;
let log : string[] = [];
return {
state :
() => [count, log] as const,
add: () => {
count ++;
log = ["Adds 1 : " + count.toString(), ...log] },
subtract: () => {
count --;
log = ["Subtracts 1 : " + count.toString(), ...log] },
getLastLog_: () => log[ log.length - 1 ]
}
}
function Counter() {
const [[count, log], {add, subtract, getLastLog_}]
= useFun( () => counterLog() );
return <>
<span>{count}</span>
<button onClick={add}>+</button>
<button onClick={subtract}>-</button>
<ul>
{log.map( (l, i) => <li key={i}>{l}</li> )}
</ul>
</>
}
function counterFun() {
let chairs = 0;
let tables = 0;
return {
state : () => ({chairs, tables}),
addChairs: () => chairs++,
subtractChairs: () => chairs--,
addTables: () => tables++,
subtractTables: () => tables--,
resetAll: () => { chairs = 0; tables = 0 }
}
}
// Storing the Fun object ---------------->
const CounterFun = counterFun();
function Chairs() {
const [{chairs}, {addChairs, subtractChairs}] = useFun( CounterFun );
return <>
<span>Chairs: {chairs}</span>
<button onClick={addChairs}>+</button>
<button onClick={subtractChairs}>-</button>
</>
}
function Tables() {
const [{tables}, {addTables, subtractTables}] = useFun( CounterFun );
return <>
<span>Tables: {tables} </span>
<button onClick={addTables}>+</button>
<button onClick={subtractTables}>-</button>
</>
}
// You can also use the stored object directly.
// This will cause re-render on Chairs and Tables component,
// but because is not a hook, will not cause a re-render on the Reset component
function Reset() {
const {resetAll} = CounterFun;
return <button onClick={resetAll}>RESET!</button>
}
function noUp( returnValue )
returns returnValue
Since functions on set collection always set an update, and state is a function that can construct an object or array every time it is called, a cancel state update signal can be set as the return value of a set function through the cancelFun noUp. This can be useful to avoid unnecessary re-renders. If you need the function return value, you can set it as a parameter of noUp method.
This does NOT UNDO the function's executed instructions. You must cancel before changing any (state) value.
import { noUp } from "use-fun";
function chairsCount() {
let chairs = 0;
let tables = 0;
return {
state : () => ({chairs, tables}),
set : {
addChairs: () => chairs >= 10 ? noUp() : chairs = chairs + 1,
subtractChairs: () => chairs <= 0 ? noUp() : chairs = chairs - 1
}
}
}
An action that returns a promise is treated as an special case, a re-render will be triggered on call and on resolve if there are changes.
function detailsFun () {
let data : any[] = [];
let isLoading = false;
return {
state : () => [data, isLoading] as const,
load : () => {
isLoading = true ;
return fetch('/api/item').then(r => r.json())
.then(r => { data = r?.data ?? []; isLoading = false })
}
}
}
If you need to trigger re-renders between cascading promises you must call another action:
return {
loadData : () => {
isLoading = true ;
return fetch('/api/item').then(r => r.json())
.then(i => {
data = i ;
this.loadDetails();
//OR: return this.loadDetails();
} )
},
loadDetails : () =>
fetch(`/api/details/${data.foo}`).then(r => r.json())
.then( d => { details = d; isLoading = false } )
}
Following examples will NOT WORK as intended:
const fun = {
// Here, a Promise is returned,
// so an update will trigger on call and on resolve.
// "data" is asigned but will not trigger an update.
// "details" is assigned and "isLoading" setted as true, triggering a update.
loadWReturn : () => {
isLoading = true ;
return fetch('/api/item').then(r => r.json())
.then(i => {
// This data assign will be visible when details resolve.
data = i ;
return fetch(`/api/details/${data.foo}`).then(r => r.json())
.then( d => { details = d; isLoading = false } )
} )
},
// In this case an update will be called immediately after resolves,
// setting "data" and triggering a state update,
// but the Fun api is unaware second fetch,
// so never triggers a re-render when it resolves,
// leaving "details" without rendering and "isLoading" rendered as true.
loadWOReturn : () => {
isLoading = true ;
return fetch('/api/item').then(r => r.json())
.then(r => {
data = r?.data ?? [];
fetch(`/api/details/${data.foo}`).then(r => r.json())
// this assign to details will not update the state.
.then( d => { details = d; isLoading = false } )
} )
},
// If you don't return the promise,
// an update will be triggered inmediately. As in common actions.
// In this case a re-render will be triggered before the promise resolves,
// even if no changes to state data is made.
// When the promise is resolved nothing will happen on render.
silentLoad: () => {
fetch(`/api/details/${data.foo}`).then(r => r.json())
.then( d => { details = d; isLoading = false } )
}
}
You can define a selector as the second argument of useFun. The component will update only if the selector result changes. Use it only when necessary, as it can harm performance.
const [ [tables, name], {addTables, subtractTables} ]
= useFun( counterFun, s => [s.tables, s.name] );
The useFun can accept an Fun collection, a function that returns it, or class that construct it. The Fun and the useFun hook can be initialized different ways:
const counter = ( count ) => ({
state: () => count,
add: () => count++,
sub: () => count--
});
function Counter() {
const [count, {add, subtract}] = useFun( () => counter(0) );
...
const counter = ( count = 0 ) => ({
state: () => count,
add: () => count++,
sub: () => count--
})
function Counter() {
const [count, {add, subtract}] = useFun( counter );
...
const counter = ( ) => {
let count = 0;
return {
state: () => count,
add: () => count++,
sub: () => count--
}
}
function Counter() {
const [count, {add, subtract}] = useFun( counter );
...
const counter = ( initValue ) => {
let count = initValue;
const state = () => count;
const set = {
add: () => count++,
sub: () => count--
};
return { state, ...set }
}
const countStore = counter(0);
function Counter() {
const [count, {add, subtract}] = useFun( countStore );
...
class CounterFun {
count = 0;
state = () => this.count;
add = () => this.count++;
sub = () => this.count--;
}
function Counter() {
const [count, {add, subtract}] = useFun( CounterFun );
...
const counterFun = {
count : 0,
state : () => count,
add : function() { this.count++ },
sub() { this.count-- }
}
function Counter() {
const [count, {add, subtract}] = useFun( counterFun );
...
// WARNING
const counter = ( count ) => ({
state: () => count,
add: () => count++,
sub: () => count--
});
function Counter() {
// This will create a new Fun collection every render.
const [count, {add, subtract}] = useFun( counter(0) );
...
You can use a Fun Store directly outside React, but is very likely that you need to enable it beforehand with the fun( collection ) method. This function returns the same object that is passed as parameter with its actions enabled to call react state updates. Usually a Fun collection is auto-enabled on his first useFun hook call.
This example uses a loader, like in Remix framework.
// storing an initialized Fun:
const details = fun(detailsFun());
export function loader = ( ) => {
details.load(); // using it without hooks
return null;
}
export default function App() {
// here details is already load or is loading.
const [[dets, isLoading], {setData, load}] = useFun( details );
...
An initialization can be done in other places, for example:
function detailsFun () {
let data : any[] = [];
let isLoading = false;
return fun({ // HERE
() => [data, isLoading] as const,
load : () => {
isLoading = true ;
fetch('/api/item').then(r => r.json())
.then(r => { r?.data ?? []; isLoading = false })
}
})
}
// OR
export function loader = ( ) => {
fun(fundetails).load(); // HERE
return null;
}
Enabling a collection is essentially binding its functions and trapping their calls to trigger a state update.
The correct way to extend a fun with new actions is to declare it as class in the first place, then others can extend this class.
class CounterFun {
count = 0;
state = () => this.count;
add = () => this.count++;
sub = () => this.count--;
}
class ExtendedCounterFun extends CounterFun {
reset = () => this.count = 0;
set = ( n : number ) => this.count = n:
}
Alternatively, you can use get and set syntax for each state variable. Then use the extendFun( Fun, extend ) utility function. Extending must be done before enabling Fun.
const classRoomSetup = ( chairs = 0, tables = 0 ) => ({
state : () => [chairs, tables] as const,
get chairs () { return chairs },
set chairs ( n : number) { chairs = n }
get tables () { return tables },
set tables ( n : number) { tables = n }
addChairs: () => chairs++,
subChairs: () => chairs--,
addTables: () => tables++,
subTables: () => tables--,
})
const extendedClassRoomSetup = extendFun( classRoomSetup, { reset() { this.chairs = 0; this.tables = 0 } } );
FAQs
Simple hook and state manager for React.
We found that use-fun demonstrated a healthy version release cadence and project activity because the last version was released less than 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.