diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d0d486..4c575d94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # MobX-React Changelog + +### 5.4.1 + +* Fixed issue where `react-is` wasn't properly rolled-up into the package. Fixes [#608](https://github.com/mobxjs/mobx-react/issues/608) + +### 5.4.0 + +* Added support for forward refs, fixes [#602](https://github.com/mobxjs/mobx-react/issues/602) + ### 5.3.6 * Fixed some additional issues around life-cycle patching, take 3. See [#536](https://github.com/mobxjs/mobx-react/pull/586) by [@xaviergonz](https://github.com/xaviergonz). Fixed [#579](https://github.com/mobxjs/mobx-react/issues/579) diff --git a/README.md b/README.md index d022d815..3bd8091a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ import { observer } from "mobx-react/custom" This package provides the bindings for MobX and React. See the [official documentation](http://mobxjs.github.io/mobx/intro/overview.html) for how to get started. +If you are using [React hooks](https://reactjs.org/docs/hooks-intro.html) with latest React 16.7 and you like living on the bleeding edge then have a look at the new [mobx-react-lite](https://github.com/mobxjs/mobx-react-lite). + ## Boilerplate projects that use mobx-react * Minimal MobX, React, ES6, JSX, Hot reloading: [MobX-React-Boilerplate](https://github.com/mobxjs/mobx-react-boilerplate) diff --git a/package.json b/package.json index d2bd797d..59949329 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mobx-react", - "version": "5.3.6", + "version": "5.4.2", "description": "React bindings for MobX. Create fully reactive components.", "main": "index.js", "jsnext:main": "index.module.js", diff --git a/src/observer.js b/src/observer.js index 6e7f8f11..43810aa5 100644 --- a/src/observer.js +++ b/src/observer.js @@ -1,7 +1,8 @@ -import React, { Component, PureComponent } from "react" +import React, { Component, PureComponent, forwardRef } from "react" import hoistStatics from "hoist-non-react-statics" import { createAtom, Reaction, _allowStateChanges, $mobx } from "mobx" import { findDOMNode as baseFindDOMNode } from "react-dom" + import EventEmitter from "./utils/EventEmitter" import inject from "./inject" import { patch as newPatch, newSymbol } from "./utils/utils" @@ -25,6 +26,10 @@ export const renderReporter = new EventEmitter() const skipRenderKey = newSymbol("skipRender") const isForcingUpdateKey = newSymbol("isForcingUpdate") +// Using react-is had some issues (and operates on elements, not on types), see #608 / #609 +const ReactForwardRefSymbol = + typeof forwardRef === "function" && forwardRef((_props, _ref) => {})["$$typeof"] + /** * Helper to set `prop` to `this` as non-enumerable (hidden prop) * @param target @@ -318,6 +323,19 @@ export function observer(arg1, arg2) { ) } + // Unwrap forward refs into `` component + // we need to unwrap the render, because it is the inner render that needs to be tracked, + // not the ForwardRef HoC + if (ReactForwardRefSymbol && componentClass["$$typeof"] === ReactForwardRefSymbol) { + const baseRender = componentClass.render + if (typeof baseRender !== "function") + throw new Error("render property of ForwardRef was not a function") + // TODO: do we need to hoist statics from baseRender to the forward ref? + return forwardRef(function ObserverForwardRef() { + return {() => baseRender.apply(undefined, arguments)} + }) + } + // Stateless function component: // If it is function but doesn't seem to be a react class constructor, // wrap it to a react class automatically diff --git a/test/__snapshots__/stateless.test.js.snap b/test/__snapshots__/stateless.test.js.snap new file mode 100644 index 00000000..191b828a --- /dev/null +++ b/test/__snapshots__/stateless.test.js.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`stateless component with forwardRef is reactive 1`] = ` +
+ result: + hello world + , + got ref + , a.x: + 2 +
+`; + +exports[`stateless component with forwardRef render test correct 1`] = ` +
+ result: + hello world + , + got ref + , a.x: + 1 +
+`; diff --git a/test/stateless.test.js b/test/stateless.test.js index 94c49a1b..f56d4c86 100644 --- a/test/stateless.test.js +++ b/test/stateless.test.js @@ -6,6 +6,8 @@ import TestUtils from "react-dom/test-utils" import * as mobx from "mobx" import { observer, propTypes } from "../src" import { createTestRoot } from "./index" +import renderer from "react-test-renderer" +import { observable } from "mobx" const testRoot = createTestRoot() @@ -74,3 +76,33 @@ test("component with observable propTypes", () => { expect(warnings.length).toBe(1) console.error = originalConsoleError }) + +describe("stateless component with forwardRef", () => { + const a = observable({ + x: 1 + }) + const ForwardRefCompObserver = observer( + React.forwardRef(({ testProp }, ref) => { + return ( +
+ result: {testProp}, {ref ? "got ref" : "no ref"}, a.x: {a.x} +
+ ) + }) + ) + + test("render test correct", () => { + const component = renderer.create( + + ) + expect(component).toMatchSnapshot() + }) + + test("is reactive", () => { + const component = renderer.create( + + ) + a.x++ + expect(component).toMatchSnapshot() + }) +})