Skip to content
This repository has been archived by the owner on Dec 31, 2020. It is now read-only.

Commit

Permalink
Implemented #111; track mapper function in
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Nov 9, 2016
1 parent eee7e1b commit 1c08739
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 8 deletions.
52 changes: 50 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => <h1>{name}</h1>

const UserNameDisplayer = inject(
stores => ({
name: stores.userStore.name
}),
NameDisplayer
)

const user = mobx.observable({
name: "Noa"
})

const App = () => (
<Provider userStore={user}>
<UserNameDisplayer />
</Provider>
)

ReactDOM.render(<App />, 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 }) => (
<Observer>
{() => <div>{user.name}</div>}
</Observer>
)
```

#### 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
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ const TodoView = observer(({todo}) => <div>{todo.title}</div>)

### `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.
Expand Down Expand Up @@ -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 }) => <h1>{name}</h1>

const UserNameDisplayer = inject(
stores => ({
name: stores.userStore.name
}),
NameDisplayer
)

const user = mobx.observable({
name: "Noa"
})

const App = () => (
<Provider userStore={user}>
<UserNameDisplayer />
</Provider>
)

ReactDOM.render(<App />, 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

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -56,4 +56,4 @@
"reactjs",
"reactive"
]
}
}
13 changes: 9 additions & 4 deletions src/inject.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { PropTypes } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import {observer} from './observer';

/**
* Store Injection
Expand All @@ -22,7 +23,6 @@ function createStoreInjector(grabStoresFn, component) {

return React.createElement(component, newProps);
}
// TODO: should have shouldComponentUpdate?
});

Injector.isInjector = true;
Expand Down Expand Up @@ -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);
};
}

16 changes: 16 additions & 0 deletions test/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})

0 comments on commit 1c08739

Please sign in to comment.