diff --git a/doc/store.md b/doc/store.md
index ae15c2de..a4f58676 100644
--- a/doc/store.md
+++ b/doc/store.md
@@ -28,7 +28,7 @@ Note: Owl's store is inspired by React Redux and VueX.
## Example
-Here is what a simple store look like:
+Here is what a simple store looks like:
```js
const actions = {
@@ -68,7 +68,7 @@ state is changed. Note that these events are triggered only after a microtask
tick, so only one event will be triggered for any number of state changes in a
call stack.
-Also, it is important to mention that the state is observed (with a `owl.Observer`),
+Also, it is important to mention that the state is observed (with an `owl.Observer`),
which is the reason why it is able to know if it was changed. This implies that
state changes need to be done carefully in some cases (adding a new key to an
object, or modifying an array with the `arr[i] = newValue` syntax). See the
@@ -146,4 +146,58 @@ const post = store.getters.getPost(id);
### Connecting a component
-Todo
\ No newline at end of file
+By default, an Owl `Component` is not connected to any store. The `connect`
+function is there to create sub Components that are connected versions of
+Components.
+
+```javascript
+const actions = {
+ increment({commit}) {
+ commit('increment', 1);
+ }
+};
+const mutations = {
+ increment({state}, val) {
+ state.counter += val;
+ }
+};
+const state = {
+ counter: 0,
+};
+const store = new owl.Store({state, actions, mutations});
+
+class Counter extends owl.Component {
+ increment() {
+ this.env.store.dispatch('increment');
+ }
+}
+function mapStoreToProps(state) {
+ return {
+ value: state.counter
+ };
+}
+const ConnectedCounter = owl.connect(mapStoreToProps)(Counter);
+
+const counter = new ConnectedCounter({ store, qweb });
+```
+```xml
+
+```
+
+The arguments of `connect` are:
+ - `Counter`: an owl `Component` to connect
+ - `mapStoreToProps`: a function that extracts the `props` of the Component
+ from the `state` of the `Store` and returns them as a dict
+ - `options`: dictionary of optional parameters that may contain
+ - `getStore`: a function that takes the `env` in arguments and returns an
+ instance of `Store` to connect to (if not given, connects to `env.store`)
+ - `hashFunction`: the function to use to detect changes in the state (if not
+ given, generates a function that uses revision numbers, incremented at
+ each state change)
+ - `deep`: [only useful if no hashFunction is given] if false, only watch
+ for top level state changes (true by default)
+
+The `connect` function returns a function that takes a `Component` as argument
+and returns a sub Class of this `Component` that is connected to the `store`
diff --git a/src/store.ts b/src/store.ts
index 853d0231..57cacbef 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -155,6 +155,7 @@ let nextID = 1;
export function connect(mapStateToProps, options: any = {}) {
let hashFunction = options.hashFunction || null;
+ const getStore = options.getStore || (env => env.store);
if (!hashFunction) {
let deep = "deep" in options ? options.deep : true;
@@ -186,19 +187,21 @@ export function connect(mapStateToProps, options: any = {}) {
const Result = class extends Comp {
constructor(parent, props?: any) {
const env = parent instanceof Component ? parent.env : parent;
+ const store = getStore(env);
const ownProps = Object.assign({}, props || {});
const storeProps = mapStateToProps(
- env.store.state,
+ store.state,
ownProps,
- env.store.getters
+ store.getters
);
const mergedProps = Object.assign({}, props || {}, storeProps);
super(parent, mergedProps);
(this.__owl__).ownProps = ownProps;
(this.__owl__).currentStoreProps = storeProps;
+ (this.__owl__).store = store;
(this.__owl__).storeHash = hashFunction(
{
- state: env.store.state,
+ state: store.state,
storeProps: storeProps,
revNumber,
deepRevNumber
@@ -214,27 +217,27 @@ export function connect(mapStateToProps, options: any = {}) {
* if we use the mounted hook, this will be done in the reverse order.
*/
_callMounted() {
- this.env.store.on("update", this, this._checkUpdate);
+ (this.__owl__).store.on("update", this, this._checkUpdate);
super._callMounted();
}
willUnmount() {
- this.env.store.off("update", this);
+ (this.__owl__).store.off("update", this);
super.willUnmount();
}
_checkUpdate() {
const ownProps = (this.__owl__).ownProps;
const storeProps = mapStateToProps(
- this.env.store.state,
+ (this.__owl__).store.state,
ownProps,
- this.env.store.getters
+ (this.__owl__).store.getters
);
const options: any = {
currentStoreProps: (this.__owl__).currentStoreProps
};
const storeHash = hashFunction(
{
- state: this.env.store.state,
+ state: (this.__owl__).store.state,
storeProps: storeProps,
revNumber,
deepRevNumber
@@ -254,9 +257,9 @@ export function connect(mapStateToProps, options: any = {}) {
_updateProps(nextProps, forceUpdate, patchQueue?: any[]) {
if ((this.__owl__).ownProps !== nextProps) {
(this.__owl__).currentStoreProps = mapStateToProps(
- this.env.store.state,
+ (this.__owl__).store.state,
nextProps,
- this.env.store.getters
+ (this.__owl__).store.getters
);
}
(this.__owl__).ownProps = nextProps;
diff --git a/tests/__snapshots__/store.test.ts.snap b/tests/__snapshots__/store.test.ts.snap
index dc440c2d..1754f5d5 100644
--- a/tests/__snapshots__/store.test.ts.snap
+++ b/tests/__snapshots__/store.test.ts.snap
@@ -1,5 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`connecting a component to store connecting a component to a local store 1`] = `""`;
+
+exports[`connecting a component to store connecting a component to a local store 2`] = `"
hello
"`;
+
exports[`connecting a component to store connecting a component works 1`] = `""`;
exports[`connecting a component to store connecting a component works 2`] = `"
hello
"`;
diff --git a/tests/store.test.ts b/tests/store.test.ts
index 88f12f82..05cecf02 100644
--- a/tests/store.test.ts
+++ b/tests/store.test.ts
@@ -476,6 +476,50 @@ describe("connecting a component to store", () => {
expect(shallowFix.innerHTML).toMatchSnapshot();
});
+ test("connecting a component to a local store", async () => {
+ env.qweb.addTemplates(`
+
+