react-shared-state-hook
Simple React shared state hook based on useState
Installation
npm i react-shared-state-hook
Basic usage
import React from ' react ' ;
import { createSharedStateHook } from ' react-shared-state-hook ' ;
const [ useSharedState , setSharedState ] = createSharedStateHook ( { a : 1 , b : 2 } ) ;
const ComponentA = ( ) => {
const state = useSharedState ( ) ;
return (
< div >
{ state . a } + { state . b }
</ div >
) ;
} ;
const ComponentB = ( ) => {
const b = useSharedState ( o => o . b ) ;
return < div > { b } </ div > ;
} ;
Example application setup
import React from ' react ' ;
import ReactDOM from ' react-dom ' ;
import { App } from ' ./App ' ;
ReactDOM . render ( React . createElement ( App ) , document . getElementById ( ' root ' ) ) ;
import React from ' react ' ;
import { useActions , useAppState } from ' ./state ' ;
export const App = ( ) => (
<>
< UsernameControl />
< CountControl />
< / >
) ;
const UsernameControl = ( ) => {
const username = useAppState ( o => o . username ) ;
const actions = useActions ( ) ;
return (
< div >
< p > Hello { username || ' ... ' } </ p >
< input value = { username ? ? ' ' } onChange = { e => actions.s etUsername ( e . target . value ) } / >
< / div >
);
};
const CountControl = ( ) => {
const count = useAppState ( o => o . count ) ;
const actions = useActions ( ) ;
return (
< div >
< p > Count: { count } </ p >
< button onClick = { ( ) => actions . increaseCount ( ) } > +1 </ button >
</ div >
) ;
} ;
import { createSharedStateHook } from ' react-shared-state-hook ' ;
import * as actions from ' ./actions ' ;
export interface AppState {
username : string | null ;
count : number ;
}
let appState : AppState = {
username : null ,
count : 1 ,
} ;
const [ useAppStateHook , setAppState ] = createSharedStateHook ( appState ) ;
const [ useActionsHook ] = createSharedStateHook ( actions ) ;
export const useAppState = useAppStateHook ;
export const useActions = useActionsHook ;
export const getState = ( ) => appState ;
export const setState = ( update : Partial < AppState > ) => {
const newState : AppState = { ... appState , ... update } ;
appState = newState ;
setAppState ( appState ) ;
} ;
import { setState , getState } from ' ./state ' ;
export function setUsername ( username : string ) {
setState ( { username } ) ;
}
export function increaseCount ( ) {
setState ( { count : getState ( ) . count + 1 } ) ;
}
Transforming data structures
When the selector function returns a new object, the component will re-render on every state update. To prevent this, either split up into multiple hook calls, pass an isEqual function or memoize state selections.
Renders on each state update:
const Component = ( ) => {
const myState = useAppState ( o => ( { a : o . count , b : o . username , t : ' ... ' } ) ) ;
return < div > .. </ div >
} ;
Using multiple hook calls (and create objects outside of selectors):
const Component = ( ) => {
const a = useAppState ( o => o . count ) ;
const b = useAppState ( o => o . username ) ;
const myState = { a , b , t : ' ... ' } ;
return < div > .. </ div >
} ;
Passing an isEqual function:
import { shallowEqual } from ' other-npm-module ' ;
const Component = ( ) => {
const myState = useAppState ( o => ( { a : o . count , b : o . username , t : ' ... ' } ) , shallowEqual ) ;
return < div > .. </ div >
} ;
Or memoize selections using something like reselect :
const myStateSelector = createSelector < AppState > (
o => o . count ,
o => o . username ,
( a , b ) => ( { a , b , t : ' ... ' } )
) ;
const Component = ( ) => {
const myState = useAppState ( myStateSelector ) ;
return < div > .. </ div >
} ;
Mixing local state with shared state
const CountControl = ( ) => {
const [ ownCount , setOwnCount ] = useState ( 0 ) ;
const countText = useAppState ( o => ` Count: ${ o . count } (current ownCount: ${ ownCount } ) ` ) ;
const ownCountText = ` Own count: ${ ownCount } ` ;
const actions = useActions ( ) ;
return (
< div >
< p > { countText } </ p >
< p > { ownCountText } </ p >
< button onClick = { ( ) => actions . increaseCount ( ) } > +1 shared </ button >
< button onClick = { ( ) => setOwnCount ( ownCount + 1 ) } > +1 own </ button >
</ div >
) ;
} ;