react-state-patterns
v1.0.0
Published
Tiny utility package for easily creating reusable implementations of React state provider patterns.
Downloads
35
Maintainers
Readme
react-state-patterns
Tiny utility package for easily creating reusable implementations of React state provider patterns.
🚀 react-state-patterns makes it easy to (and reduces boilerplate) create implementations of common React state provider patterns.
⚠️ Powered by React Hooks under the hood. (This library has a peer dependency on react: ^16.8.0
)
Getting Started
Install
npm install react-state-patterns --save
Creating State Patterns
Directly From Hook
import useProviders, { hookSchema } from '@procore/react-state-patterns';
// Create the state patterns
const Counter = statePatterns(props => {
const [count, setCount] = useState(props.initialValue || 0);
const handlers = {
incrementBy: value => setCount(count + value),
decrementBy: value => setCount(count - value)
};
// hookSchema(...)
// => { counter: { state: { count: 0 }, handlers: { incrementBy: (v) => {...}, decrementBy: (v) => {...} } } }
return hookSchema({ count: count }, handlers, "counter");
});
// Counter = { useHook, withState, State, Provider, Consumer }
Using useStateHook
util
import useProviders, { useStateHook } from '@procore/react-state-patterns';
// Create the state patterns
const Counter = useProviders(
useStateHook(
(props) => ({ count: props.initialValue || 0 }),
{
incrementBy: state => value => ({ ...state, count: state.count + value }),
decrementBy: state => value => ({ ...state, count: state.count - value })
},
"counter"
)
);
// Counter = { useHook, withState, State, Provider, Consumer }
Use the patterns
Decorator Pattern
const Displayer = ({ counter: { state, handlers }}) => (
<React.Fragment>
<div>{state.count}</div>
<button onClick={() => handlers.decrementBy(1)}>Decrement</button>
<button onClick={() => handlers.incrementBy(1)}>Increment</button>
</React.Fragment>
);
const StatefulDisplayer = Counter.withState(Displayer);
const rootElement = document.getElementById("root");
ReactDOM.render(<StatefulDisplayer initialValue={5} />, rootElement);
Render Prop Pattern
const Displayer = (props) => (
<Counter.State initialValue={5}>
{({ counter: { state, handlers } }) => (
<React.Fragment>
<div>{state.count}</div>
<button onClick={() => handlers.decrementBy(1)}>Decrement</button>
<button onClick={() => handlers.incrementBy(1)}>Increment</button>
</React.Fragment>
)}
</Counter.State>
);
Context Provider/Consumer Pattern
const Displayer = (props) => (
<Counter.Provider initialValue={5}>
<Counter.Consumer>
{({ counter: { state, handlers } }) => (
<React.Fragment>
<div>{state.count}</div>
<button onClick={() => handlers.decrementBy(1)}>Decrement</button>
<button onClick={() => handlers.incrementBy(1)}>Increment</button>
</React.Fragment>
)}
</Counter.Consumer>
</Counter.Provider>
);
Custom Hook Pattern
const Displayer = (props) => {
const { counter: { state, handlers } } = Counter.useHook({ initialValue: 5 });
return (
<React.Fragment>
<div>{state.count}</div>
<button onClick={() => handlers.decrementBy(1)}>Decrement</button>
<button onClick={() => handlers.incrementBy(1)}>Increment</button>
</React.Fragment>
);
};
Code Style Guides
Prettier is run as a pre-commit hook to automatically
modify staged .js
and .jsx
files to adhere to base code style rules defined in the .prettierrc
.
Eslint is also used as an in-editor linter, so be sure to install an appropriate Eslint Plugin for your editor of choice. Prettier rules are setup to take precedence and override any conflicting eslint rules.