diff --git a/README.md b/README.md
index 8bf134d3..408c01b8 100644
--- a/README.md
+++ b/README.md
@@ -83,8 +83,6 @@ The rendering in the function will be tracked and automatically re-rendered when
This can come in handy when needing to pass render function to external components (for example the React Native listview), or if you
dislike the `observer` decorator / function.
-Example:
-
```javascript
class App extends React.Component {
render() {
@@ -105,6 +103,53 @@ React.render(, document.body)
person.name = "Mike" // will cause the Observer region to re-render
```
+In case you are a fan of render props, you can use that instead of children. Be advised, that you cannot use both approaches at once, children have a precedence.
+Example
+
+```javascript
+class App extends React.Component {
+ render() {
+ return (
+
+ {this.props.person.name}
+
{this.props.person.name}
}
+ />
+
+ )
+ }
+}
+
+const person = observable({ name: "John" })
+
+React.render(, document.body)
+person.name = "Mike" // will cause the Observer region to re-render
+```
+Observer can also inject the stores simply by passing a selector function.
+Example with inject
+
+```javascript
+
+const NameDisplayer = ({ name }) => {name}
+
+const user = mobx.observable({
+ name: "Noa"
+})
+
+const UserNameDisplayer = ()=>(
+ ({user:stores.user})}
+ render={props => ()}
+ />
+)
+
+const App = () => (
+
+
+
+)
+```
+
### Global error handler with `onError`
If a component throws an error, this logs to the console but does not 'crash' the app, so it might go unnoticed.
@@ -438,3 +483,5 @@ Data will have one of the following formats:
WeakMap. Its `get` function returns the associated reactive component of the given node. The node needs to be precisely the root node of the component.
This map is only available after invoking `trackComponents`.
+
+
diff --git a/src/index.d.ts b/src/index.d.ts
index b687cc3e..69ff1f16 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -65,7 +65,7 @@ export function onError(cb: (error: Error) => void): () => void
export class Provider extends React.Component {}
-export class Observer extends React.Component<{ children?: () => React.ReactNode }, {}> {}
+export class Observer extends React.Component<{ children?: () => React.ReactNode, render?: () => React.ReactNode, inject?: IStoresToProps | string[] }, {}> {}
export function useStaticRendering(value: boolean): void
diff --git a/src/observer.js b/src/observer.js
index ac44c7b9..de1517bc 100644
--- a/src/observer.js
+++ b/src/observer.js
@@ -1,5 +1,5 @@
import { Atom, Reaction, extras } from "mobx"
-import { Component } from "react"
+import React, { Component } from "react"
import { findDOMNode as baseFindDOMNode } from "react-dom"
import EventEmitter from "./utils/EventEmitter"
import inject from "./inject"
@@ -349,22 +349,42 @@ function mixinLifecycleEvents(target) {
}
// TODO: support injection somehow as well?
-export const Observer = observer(({ children }) => children())
+export const Observer = observer(({ children, inject: observerInject, render }) => {
+ const component = children || render
+ if (typeof component === "undefined") {
+ return null
+ }
+ if (!observerInject) {
+ return component()
+ }
+ const InjectComponent = inject(observerInject)(component)
+ return
+})
Observer.displayName = "Observer"
-Observer.propTypes = {
- children: (propValue, key, componentName, location, propFullName) => {
- if (typeof propValue[key] !== "function")
- return new Error(
- "Invalid prop `" +
- propFullName +
- "` of type `" +
- typeof propValue[key] +
- "` supplied to" +
- " `" +
- componentName +
- "`, expected `function`."
- )
+const ObserverPropsCheck = (props,key,componentName,location,propFullName)=>{
+ const extraKey = key === "children" ? "render" : "children"
+ if(typeof propValue[key] === "function" && typeof propValue[extraKey] === "function" ){
+ return new Error("Invalid prop,do not use children and render in the same time in`" + componentName )
}
+
+ if (typeof propValue[key] === "function" || typeof propValue[extraKey] === "function") {
+ return
+ }
+ return new Error(
+ "Invalid prop `" +
+ propFullName +
+ "` of type `" +
+ typeof propValue[key] +
+ "` supplied to" +
+ " `" +
+ componentName +
+ "`, expected `function`."
+ )
+}
+
+Observer.propTypes = {
+ render: ObserverPropsCheck,
+ children: ObserverPropsCheck,
}
diff --git a/test/observer.test.js b/test/observer.test.js
index 3b47b733..ecde7ed4 100644
--- a/test/observer.test.js
+++ b/test/observer.test.js
@@ -4,8 +4,8 @@ import ReactDOM from "react-dom"
import ReactDOMServer from "react-dom/server"
import TestUtils from "react-dom/test-utils"
import * as mobx from "mobx"
-import { observer, inject, onError, offError, useStaticRendering, Observer } from "../"
-import { createTestRoot, sleepHelper, asyncReactDOMRender } from "./index"
+import { observer, inject, onError, offError, useStaticRendering, Observer, Provider } from "../"
+import { createTestRoot, sleepHelper, asyncReactDOMRender, asyncRender } from "./"
import ErrorCatcher from "./ErrorCatcher"
/**
@@ -16,12 +16,6 @@ const testRoot = createTestRoot()
const getDNode = (obj, prop) => obj.$mobx.values[prop]
-const asyncRender = (element, root) => {
- return new Promise(resolve => {
- ReactDOM.render()
- })
-}
-
/*
use TestUtils.renderIntoDocument will re-mounted the component with with different props
some misunderstanding will be cause?
@@ -209,7 +203,6 @@ describe("does not views alive when using static rendering", () => {
})
test("no re-rendering on static rendering", () => {
- expect(renderCount).toBe(1)
data.z = "hello"
expect(renderCount).toBe(1)
expect(TestUtils.findRenderedDOMComponentWithTag(element, "div").innerHTML).toBe("hi")
@@ -533,9 +526,10 @@ describe("it rerenders correctly if some props are non-observables - 2", () => {
}
mobx.reaction(() => odata.x, v => console.log(v))
-
- beforeAll(async () => {
+
+ beforeAll(async done => {
await asyncReactDOMRender(, testRoot)
+ done()
})
test("init renderCount === 1", () => {
@@ -757,3 +751,60 @@ test.skip("195 - should throw if trying to overwrite lifecycle methods", () => {
/Cannot assign to read only property 'componentWillMount'/
)
})
+
+describe("use Observer inject and render sugar should work ", () => {
+ test("use render without inject should be correct", async () => {
+ const Comp = () => (
+
+ {123}} />
+
+ )
+ await asyncReactDOMRender(, testRoot)
+ expect(testRoot.querySelector("span").innerHTML).toBe("123")
+ })
+
+ test("use children without inject should be correct", async () => {
+ const Comp = () => (
+
+ {props => {123}}
+
+ )
+ await asyncReactDOMRender(, testRoot)
+ expect(testRoot.querySelector("span").innerHTML).toBe("123")
+ })
+
+ test("use render with inject should be correct", async () => {
+ const Comp = () => (
+
+ ({ h: store.h, w: store.w })}
+ render={props => {`${props.h} ${props.w}`}}
+ />
+
+ )
+ const A = () => (
+
+
+
+ )
+ await asyncReactDOMRender(, testRoot)
+ expect(testRoot.querySelector("span").innerHTML).toBe("hello world")
+ })
+
+ test("use children with inject should be correct", async () => {
+ const Comp = () => (
+
+ ({ h: store.h, w: store.w })}>
+ {props => {`${props.h} ${props.w}`}}
+
+
+ )
+ const A = () => (
+
+
+
+ )
+ await asyncReactDOMRender(, testRoot)
+ expect(testRoot.querySelector("span").innerHTML).toBe("hello world")
+ })
+})
diff --git a/test/ts/compile-ts.tsx b/test/ts/compile-ts.tsx
index 83994866..4c1aa0ce 100644
--- a/test/ts/compile-ts.tsx
+++ b/test/ts/compile-ts.tsx
@@ -191,6 +191,18 @@ class ObserverTest extends Component {
}
}
+class ObserverTest2 extends Component {
+ render() {
+ return test
} />;
+ }
+}
+
+class ObserverTest3 extends Component {
+ render() {
+ return test
} />;
+ }
+}
+
@observer
class ComponentWithoutPropsAndState extends Component<{}, {}> {
componentDidUpdate() {