diff --git a/CHANGELOG.md b/CHANGELOG.md
index 508de030..9fa510d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,14 +2,62 @@
### 4.0.0
-* Introduced `useStaticRendering(boolean)` to better support server-side rendering scenerios. See [#140](https://github.com/mobxjs/mobx-react/issues/140)
-* Introduced `Observer`. Can be used as alternative to the `observer` decorator. Marks a component region as reactiove. See the Readme / [#138](https://github.com/mobxjs/mobx-react/issues/138)
+#### `inject(fn, component)` will now track `fn` as well
+
+`inject(func)` is now reactive as well, that means that transformation in the selector function will be tracked, see [#111](https://github.com/mobxjs/mobx-react/issues/111)
+
+```javascript
+const NameDisplayer = ({ name }) =>
{name}
+
+const UserNameDisplayer = inject(
+ stores => ({
+ name: stores.userStore.name
+ }),
+ NameDisplayer
+)
+
+const user = mobx.observable({
+ name: "Noa"
+})
+
+const App = () => (
+
+
+
+)
+
+ReactDOM.render(, document.body)
+```
+
+#### Better support for Server Side Rendering
+
+Introduced `useStaticRendering(boolean)` to better support server-side rendering scenerios. See [#140](https://github.com/mobxjs/mobx-react/issues/140)
+
+#### Introduced `Observer` as alternative syntax to the `observer` decorator.
+
+_This feature is still experimental and might change in the next minor release, or be deprecated_
+
+Introduced `Observer`. Can be used as alternative to the `observer` decorator. Marks a component region as reactiove. See the Readme / [#138](https://github.com/mobxjs/mobx-react/issues/138)
+Example:
+
+```javascript
+const UserNameDisplayer = ({ user }) => (
+
+ {() => {user.name}
}
+
+)
+```
+
+#### Other improvements
+
* Clean up data subscriptions if an error is thrown by an `observer` component, see [#134](https://github.com/mobxjs/mobx-react/pull/134) by @andykog
* Print warning when `inject` and `observer` are used in the wrong order, see #146, by @delaetthomas
* export `PropTypes` as well, fixes #153
* Add react as a peer dependency
* Added minified browser build: `index.min.js`, fixes #147
+---
+
### 3.5.8
* Fixed issue where `props` where not passed properly to components in very rare cases. Also fixed #115
diff --git a/README.md b/README.md
index 8c884944..20f584a6 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,8 @@ const TodoView = observer(({todo}) => {todo.title}
)
### `Observer`
+_This feature is still experimental and might change in the next minor release, or be deprecated_
+
`Observer` is a React component, which applies `observer` to an unanymous region in your component.
It takes as children a single, argumentless function which should return exactly one React component.
The rendering in the function will be tracked and automatically be re-rendered when needed.
@@ -254,7 +256,40 @@ class MyComponent extends React.Component {
}
```
+#### Customizing inject
+
+Instead of passing a list of store names, it is also possible to create a custom mapper function and pass it to inject.
+The mapper function receives all stores as argument, the properties with which the components are invoked and the context, and should produce a new set of properties,
+that are mapped into the original:
+
+`mapperFunction: (allStores, props, context) => additionalProps`
+
+Since version 4.0 the `mapperFunction` itself is tracked as well, so it is possible to do things like:
+
+```javascript
+const NameDisplayer = ({ name }) => {name}
+
+const UserNameDisplayer = inject(
+ stores => ({
+ name: stores.userStore.name
+ }),
+ NameDisplayer
+)
+
+const user = mobx.observable({
+ name: "Noa"
+})
+
+const App = () => (
+
+
+
+)
+
+ReactDOM.render(, document.body)
+```
+_N.B. note that in this *specific* case neither `NameDisplayer` or `UserNameDisplayer` doesn't need to be decorated with `observer`, since the observable dereferencing is done in the mapper function_
#### Strongly typing inject
diff --git a/package.json b/package.json
index a566445a..8f10e640 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mobx-react",
- "version": "3.5.9",
+ "version": "4.0.0-beta.2",
"description": "React bindings for MobX. Create fully reactive components.",
"main": "index.js",
"typings": "index",
@@ -56,4 +56,4 @@
"reactjs",
"reactive"
]
-}
\ No newline at end of file
+}
diff --git a/src/inject.js b/src/inject.js
index fc0bc203..2eac2319 100644
--- a/src/inject.js
+++ b/src/inject.js
@@ -1,5 +1,6 @@
import React, { PropTypes } from 'react';
import hoistStatics from 'hoist-non-react-statics';
+import {observer} from './observer';
/**
* Store Injection
@@ -22,7 +23,6 @@ function createStoreInjector(grabStoresFn, component) {
return React.createElement(component, newProps);
}
- // TODO: should have shouldComponentUpdate?
});
Injector.isInjector = true;
@@ -77,14 +77,19 @@ export default function inject(/* fn(stores, nextProps) or ...storeNames */) {
let grabStoresFn;
if (typeof arguments[0] === "function") {
grabStoresFn = arguments[0];
+ return function(componentClass) {
+ // mark the Injector as observer, to make it react to expressions in `grabStoresFn`,
+ // see #111
+ return observer(createStoreInjector(grabStoresFn, componentClass));
+ };
} else {
const storesNames = [];
for (let i = 0; i < arguments.length; i++)
storesNames[i] = arguments[i];
grabStoresFn = grabStoresByName(storesNames);
+ return function(componentClass) {
+ return createStoreInjector(grabStoresFn, componentClass);
+ };
}
- return function(componentClass) {
- return createStoreInjector(grabStoresFn, componentClass);
- };
}
diff --git a/test/inject.js b/test/inject.js
index 05692b64..b5dd85e1 100644
--- a/test/inject.js
+++ b/test/inject.js
@@ -327,5 +327,21 @@ test('inject based context', t => {
t.end();
})
+ test('using a custom injector is reactive', t => {
+ const user = mobx.observable({ name: "Noa"})
+ const mapper = (stores) => ({ name: stores.user.name })
+
+ const DisplayName = (props) => e("h1", {}, props.name)
+ const User = inject(mapper)(DisplayName)
+ const App = () => e(Provider, { user: user }, e(User, {}))
+
+ const wrapper = mount(e(App))
+ t.equal(wrapper.find("h1").text(), "Noa")
+
+ user.name = "Veria"
+ t.equal(wrapper.find("h1").text(), "Veria")
+ t.end()
+ })
+
t.end()
})
\ No newline at end of file