Redhooks is tiny React utility library for holding a predictable state container in your React apps. Inspired by Redux, it reimplements the redux paradigm of state-management by using React's new Hooks and Context API, which have been officially released by the React team.
- Motivation
- Basic Example
- Apply Middleware
- Usage with React Router
- Isolating Redhooks Sub-Apps
- Redhooks API Reference
- CodeSandbox Examples
- License
Installation
npm install --save redhooks
Motivation
In the Reactjs docs a nice paragraph titled useYourImagination() suggests to think of different possible usages of functionality Hooks provide, which is essentially what Redhooks tries to do.
This package does not use any third party library, being only dependendent upon the Hooks and the Context API.
You do not need to install react-redux
to connect your components to the store because you can have access to it directly from any of your function components by utilizing the useStore
Redhooks API.
Hooks are not allowed within class Components, for using the store within them Redhooks exposes a Higher Order Component (HOC) named connect
.
It also supports the use of middleware like redux-thunk
or redux-saga
or your own custom middleware conforming to the middleware's API.
Basic Example
Redhooks follows the exact same principles of redux, which was the package's inspiration.
- the total state of your app is stored in an object tree inside of a single store object.
- state is read only, so the only way to change the state is to dispatch an action, an object describing the changes to be made to the state.
- to specify how the actions transform the state tree, you write pure reducers.
Store
store.js
; // function reducerconst hello = state = phrase: "good morning" type payload ; // function reducerconst counter = { }; // You can use the combineReducers functionconst rootReducer = ;const store = ; // or if you want to be less verbose you can pass a plain object whose values are reducer functionsconst store = ; // eventually we can pass to createStore as second arg an opts object like:// const opts = { preloadedState: { counter: 10 }, initialAction: { type: "INCREMENT" } }// const store = createStore(rootReducer, opts); ;
App.js
;; { return <Provider store=store> <DispatchAction /> <DispatchActionExpensive /> <ReadFromStore /> </Provider> ;} const rootElement = document;ReactDOM;
Dispatching Sync and Async Actions - Non-expensive rendering operation
If your component does not require expensive rendering, you can use the useStore
Redhooks API within your
functional component in order to access the Redhooks store. Class or function components that perform expensive rendering operations can be connected to the store by using the connect
Redhooks HOC which takes care to avoid unnecessary re-rendering in order to improve performance. We'll be able to see this in action below:
./components/DispatchAction.js
;; const DispatchAction = { const dispatch = ; return <div> <button onClick= > Say Hello </button> <button onClick= > Sync Increment Counter </button> <button onClick= > Async Decrement Counter </button> </div> ;}; ;
Dispatching Sync and Async Actions - Expensive rendering operation
For components requiring expensive rendering, the use of connect
helps to avoid any unnecessary re-rendering.
./components/DispatchActionExpensive.js
;; const DispatchActionExpensive = <div> <button onClick= props> Sync Increment Counter </button> <button onClick= > Async Decrement Counter </button> </div>; DispatchActionExpensive;
Use Store from a Function Component
./components/ReadFromStore.js
;; const ReadFromStore = { const state = ; const hello counter = state; return <section> <h1>hellophrase</h1> <span>counter</span> </section> ;}; ;
Tip: If your function component requires an expensive render, you should use the
connect
HOC Redhooks API.
Use Store from a Class Component
;; { const hello counter = thisprops; return <section> <h1>hellophrase</h1> <span>counter</span> </section> ; }; { return hello: statehello counter: statecounter ;} mapStateToPropReadFromStore;
Apply Middleware
As for Redux, middleware is a way to extend Redhooks with custom functionality.
Middleware are functions which receive the store's dispatch
and getState
as named arguments, and subsequently return a function. Redhooks supports the use of redux middleware like redux-thunk, redux-saga or you could write custom middleware to conform to the middleware API.
Custom middleware - Logger Example
const logger = { console let result = console return result}
redux-thunk
and redux-saga
Use ;;;;;; const sagaMiddleware = ; const middlewares = thunk sagaMiddleware; const store = ; { console;}// redux-saga needs to run as soon the store is readystore sagaMiddleware; ;
Usage with React Router
App routing can be handled using React Router.
const App = <Provider store=store> <Router> <Switch> <Route exact path="/" component=Home /> <Route exact path="/about" component=About /> </Switch> </Router> </Provider>
; const opts = preloadedState: counter: 9 initialAction: type: "INCREMENT" middlewares: thunk; const store =
Isolating Redhooks Sub-Apps
;;;; const counter = { }; const store = ; { return <Provider store=store> <DispatchAction /> <ReadFromStore /> </Provider> ;}
Each instance will be independent and it will have its own store.
;; { return <ReactFragment> <SubApp /> <SubApp /> </ReactFragment> ;} const rootElement = document;ReactDOM;
Redhooks API Reference
createStore
createStore
returns the store object to be passed to the <Provider store={store} />
.
- The
reducer
argument might be a single reducer function, a function returned bycombineReducers
or a plain object whose values are reducer functions (if your store requires multiple reducers). - The
opts
optional argument is an object which allows you to pass apreloadedState
,initialAction
andmiddlewares
.
The store is ready when the
Provider
is mounted, after which anonload
event will be triggered.
Example
const opts = preloadedState: 1 initialAction: type: "DECREMENT" middlewares: thunk sagaMiddleware logger;const store = ;store ;
combineReducers
combineReducers
combines an object whose props are different reducer functions, into a single reducer function
- The
reducers
argument is an object whose values correspond to different reducing functions that need to be combined into one.
Example
const rootReducer = const store =
connect
connect
function connects a React component to a Redhooks store. It returns a connected component class that wraps the component you passed in taking care to avoid unnecessary re-rendering. It should be used if your class or function components require expensive rendering.
- If a
mapStateToProps
function is passed, your component will be subscribed to Redhooks store. Any time the store is updated,mapStateToProps
will be called. The results ofmapStateToProps
must be a plain object, which will be merged into your component’s props. If you don't want to connect to Redhooks store, pass null or undefined in place of mapStateToProps. mapDispatchToProps
, if passed, may be either a function that returns a plain object whose values, themselves, are functions or a plain object whose values are action creator functions. In both cases the props of the returned object will be merged in your component’s props. If is not passed your component will receivedispatch
prop by default.
Example
const mapStateToProps = counter: statecounter const mapDispatchToProps = // orconst mapDispatchToProps = type mapStateToProps mapDispatchToPropsReactComponent
bindActionCreators
bindActionCreators
turns an object whose values are action creators into an object with the
same keys, but with every function wrapped in a dispatch
call so they
may be invoked directly.
actionCreators
An object whose values are action creator functions or plain objects whose values are action creator functionsdispatch
The dispatch function available on your Redhooks store.
Action creator
An action creator is a function that creates an action.
type Action | AsyncAction
Example
actions.js
const action_1 = action_1const action_2 = action_2const action_3 = action_3
YourComponentConnected.js
;;; const YourComponent = <div> <h1>counter</h1> <button onClick=actionsaction_1>action_1</button> <button onClick=actionsaction_2>action_2</button> <button onClick=actionsaction_2>action_3</button> </div>; const mapStateToProps = counter: statecounter; // a verbose wayconst mapDispatchToProps = actions: ; mapStateToProps mapDispatchToPropsYourComponent; // or simply mapStateToProps actions YourComponent;
Provider
The <Provider />
makes the Redhooks store available to any nested components.
Example
;;;;; { return <Provider store=store> <DispatchAction /> <ReadFromStore /> </Provider> ;}
useStore
useStore
can be only used within a function component and it returns the store
.
- The
store
is an object whose props are thestate
and thedispatch
.
Example
;; const Example = { const state dispatch = ; // do not use it within a Class Component const counter = state; return <section> <span>counter</span> <button onClick= > Increment Counter </button> </section> ;}; ;
CodeSandbox Examples
Following few open source projects implemented with redux
have been migrated to redhooks
:
- Shopping Cart: Sandbox
- TodoMVC: Sandbox
- Tree-View: Sandbox
- Saga-Middleware: Sandbox
- Redux-Thunk: Sandbox
License
This software is free to use under the MIT license. See the LICENSE file for license text and copyright information.