Skip to content

Commit 04f5976

Browse files
toto8514toto8514
authored andcommitted
createStore(reducer) - reducer accepts plain obj
1 parent 6dd5f38 commit 04f5976

File tree

5 files changed

+33
-14
lines changed

5 files changed

+33
-14
lines changed

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# <img src='https://raw.githubusercontent.com/iusehooks/redhooks/master/logo/logo.png' width="224" height='61' alt='Redhooks Logo' />
22

33
Redhooks is tiny React utility library for holding a predictable state container in your React apps.
4-
Inspired by https://redux.js.org, it reimplements reduxjs concept using the experimental Hooks API and the Context API.
4+
Inspired by [Redux](https://redux.js.org), it reimplements redux concept by using the experimental Hooks API and the Context API.
55

66
- [Motivation](#motivation)
77
- [Basic Example](#basic-example)
@@ -21,7 +21,7 @@ npm install --save redhooks
2121

2222
# Motivation
2323

24-
In the https://reactjs.org/docs/hooks-custom.html docs a nice paragraph titled useYourImagination() suggests to think on differents possible usages of the Hooks, this essentially is what Redhooks tries to do.
24+
In the [Reactjs docs](https://reactjs.org/docs/hooks-custom.html) a nice paragraph titled useYourImagination() suggests to think on differents possible usages of the Hooks, this essentially is what Redhooks tries to do.
2525
Redhooks does not use any third party library, it only depends on the new Hooks and the Context API.
2626
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 calling `useStore` Redhooks api.
2727
Hooks are not allowed within class Components, for using the store within them Redhooks exposes a HOC named `connect`.
@@ -65,12 +65,16 @@ const counter = (state = 0, { type, payload }) => {
6565
}
6666
};
6767

68+
// You can use the combineReducers function
6869
const rootReducer = combineReducers({ hello, counter });
69-
7070
const store = createStore(rootReducer);
71+
72+
// or if you want to be less verbose you can pass a plain object whose values are reducer functions
73+
const store = createStore({ hello, counter });
74+
7175
// eventually we can pass to createStore as second arg an opts object like:
7276
// const opts = { preloadedState: { counter: 10 }, initialAction: { type: "INCREMENT" } }
73-
// const store = createStore(rootReducer. opts);
77+
// const store = createStore(rootReducer, opts);
7478

7579
export default store;
7680
```
@@ -135,7 +139,7 @@ export default DispatchAction;
135139
```
136140

137141
## Dispatching Sync and Async Actions - Expensive rendering operation
138-
For components wich perform expensive rendering the use of `connect` HOC helps to avoid unnecessary re-rendering.
142+
For components wich perform expensive rendering the use of `connect` HOC helps to avoid unnecessary re-renders.
139143

140144
`./components/DispatchActionExpensive.js`
141145
```js
@@ -365,8 +369,7 @@ ReactDOM.render(<App />, rootElement);
365369
createStore(reducer, [opts])
366370
```
367371
`createStore` returns the store object to be passed to the `<Provider store={store} />`.
368-
* The `reducer` argument is the is your reducer function or a function returned by `combineReducers` if your
369-
store needs more then one reducer.
372+
* The `reducer` argument might be a single reducer function, a function returned by `combineReducers` or a plain object whose values are reducre functions if your store needs multiple reducers.
370373
* The `opts` optional argument is an object which allows you to pass a `preloadedState`, `initialAction` and `middlewares`.
371374
> The store is ready after the Provider is mounted, an `onload` event will be triggered at that time.
372375

__tests__/createStore.spec.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { createStore } from "../src/store";
2-
import { counterReducer } from "./helpers/reducers";
2+
import { counterReducer, plainReducersObj } from "./helpers/reducers";
33

44
describe("createStore", () => {
5-
it("should create a store and initialized the state given a reducer function", () => {
6-
const store = createStore(counterReducer);
5+
it("should create a store with the given reducer function or plain object", () => {
6+
let store = createStore(counterReducer);
77
expect(store.initialState).toBe(0);
8+
store = createStore(plainReducersObj);
9+
expect(store.initialState.counterReducer).toBe(0);
10+
expect(store.initialState.genericReducer).toEqual({ a: 1 });
11+
expect(store.initialState.todoReducer).toEqual([]);
812
});
913

10-
it("should throw if the reducer passed as argument is not a function", () => {
14+
it("should throw if the reducer passed as argument is not a function or plain object", () => {
1115
expect(() => createStore(false)).toThrow();
1216
expect(() => createStore(undefined)).toThrow();
1317
expect(() => createStore(null)).toThrow();

__tests__/helpers/reducers.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ export const genericReducer = (state = { a: 1 }, { type, payload }) => {
2626
return state;
2727
}
2828
};
29+
30+
export const plainReducersObj = {
31+
genericReducer,
32+
todoReducer,
33+
counterReducer
34+
};

src/combineReducers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function invalidStateErrorMessage(key, action) {
7373

7474
function areValidReducers(validReducers, reducersKeys) {
7575
if (validReducers.length === 0) {
76-
return "The object passed as argument does not have valid reducer functions";
76+
return "The object passed as argument does not have any valid reducer functions";
7777
}
7878

7979
if (validReducers.length !== reducersKeys.length) {

src/store.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createContext, useContext } from "react";
2+
import combineReducers from "./combineReducers";
23
import isPlainObject from "./utils/isPlainObject";
34
import objValueFunc from "./utils/objValueFunc";
45

@@ -14,8 +15,9 @@ const Context = createContext();
1415
* parts of the state tree respond to actions, you may combine several reducers
1516
* into a single reducer function by using `combineReducers`.
1617
*
17-
* @param {Function} reducer A function that given an action and the current state it returns the next state tree.
18-
*
18+
* @param {Function|Object} reducer A function that given an action and the current state it returns the next state tree.
19+
* In case of multiple reducers this function may be created either by using `combineReducers` or by passing a plain object
20+
* whose values are reducer functions.
1921
* @param {Object} opts You may optionally specify a preloadedState and/or dispatch an initilaAction and/or middlewares
2022
* @param {Object} opts.preloadedState - To initialize the store with a State.
2123
* @param {Object} opts.initilaAction - To dispatch an initialAction to the Store => { type: "INIT", payload: "Hello" }.
@@ -25,6 +27,10 @@ const Context = createContext();
2527
*/
2628

2729
export const createStore = (reducer, opts = {}) => {
30+
if (isPlainObject(reducer)) {
31+
reducer = combineReducers(reducer);
32+
}
33+
2834
if (typeof reducer !== "function") {
2935
throw new Error("The reducer must be a function");
3036
}

0 commit comments

Comments
 (0)