- Generate
useSeletector
hook for any react state (useState
/useReducer
/etc...) - Generate
getState
function for any react state
npm install --save react-create-state-selector
import React from 'react';
import { render } from 'react-dom';
import { createSelectorHook } from 'react-create-state-selector';
const reducer = (state: { count: number; str: string }, action: { type: 'count' | 'str' }) => {
if (action.type === 'count') {
return { ...state, count: state.count + 1 };
}
if (action.type === 'str') {
return { ...state, str: '' + Math.random() };
}
return state;
};
function useCounter() {
let [state, dispatch] = React.useReducer(reducer, { count: 0, str: 'none' });
const useSelector = createSelectorHook(state);
return {
useSelector,
dispatch,
};
}
export const CounterDisplay = () => {
const { useSelector, dispatch } = useCounter();
const count = useSelector((s) => {
return { count: s.count };
});
const doubleCount = useSelector((s) => s.count * 2);
const str = useSelector((s) => s.str);
return (
<div>
<div>objectCount: {JSON.stringify(count)}</div>
<div>count x2: {doubleCount}</div>
<div>str: {str}</div>
<button onClick={() => dispatch({ type: 'count' })}>count = count + 1</button>
<button onClick={() => dispatch({ type: 'str' })}>str = Math.random</button>
</div>
);
};
function App() {
return <CounterDisplay />;
}
render(<App />, document.getElementById('root'));
Allows you to generate useSelector
hook function, so you won't need to pass the state
and could subscribe only for selected part of state.
Just like react-redux useSelector()
import { createSelectorHook } from 'react-create-state-selector'
function useCustomHook() {
const [state, setState] = React.useState({ count: 0 }); // or use useReducer
const useSelector = createSelectorHook(state);
// @NOTE: You may pass this as context value and use as store
// see: https://github.com/jamiebuilds/unstated-next
return { useSelector, setState }
}
function Component() {
const { useSelector, setState } = useCustomHook();
// @NOTE: Memoized until selector(state) returned value has changed
const count = useSelector(state => state.count);
return <div>
<button onClick={() => setState(state => ({ count: state.count + 1 }))}>
{count}
</button>
</div>
}
Caveats (!)
Function createSelectorHook
uses deepEqual comparison by default. So if you want to use another comparison method (e.g. react-redux shallowEqual), then you should pass it as second argument to createSelectorHook
.
Allows you to generate getState
function, so you won't need to pass the state
outside directly and could get previous state before calling your dispatch function.
import { createGetStateFunction } from 'react-create-state-selector'
const reducer = (state: { count: number; }, action: { type: 'count' }) => {
if (action.type === 'count') {
return { ...state, count: action.payload };
}
return state;
};
function useCustomHook() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
const getState = createGetStateFunction(state);
// @NOTE: You may pass this as context value and use as store
// see: https://github.com/jamiebuilds/unstated-next
return { getState, dispatch }
}
function Component() {
const { getState, dispatch } = useCustomHook();
const doWithState = (callback: (state: ReturnType<typeof getState>) => any) => {
const state = getState();
return callback(state);
}
return <div>
<button onClick={() => alert(JSON.stringify(getState(), null, 2))}>
show current state
</button>
<button onClick={() => doWithState(state => dispatch({ type: 'count', payload: state.count + 1 }))}>
add +1 to state.count using current state
</button>
</div>
}