Skip to content

Fix ReactFiberReconciler annotation to include PI #8751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 30, 2017

Conversation

iamdustan
Copy link
Contributor

Fixing up @sebmarkbage comments at #8628 (comment)

@@ -189,7 +189,7 @@ var TestRenderer = ReactFiberReconciler({

useSyncScheduling: true,

getPublicInstance(inst) {
getPublicInstance(inst : Instance | TextInstance) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have the return value here too to guarantee that the inference is correct. Otherwise, if it gets passed somewhere else, it might be inferred to be something very simple (like an empty object) and passes everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do I say the return value is Instance | TextInstance | createNodeMockReturnValue?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. Just put getPublicInstance(inst : Instance | TextInstance) : * then for "inferred". That way if createNodeMock gets a proper type in the future, it'll get caught.

@@ -170,7 +170,7 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T

deferredUpdates,

getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | I | TI | null) {
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | I | TI | PI | null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this method is the same as for refs except for the return value on the very top level. var inst = ReactDOM.render(...)

It should never be possible to get a handle on an instance of I or TI since those are internal instances to the renderer. The point of getPublicInstance is to ensure that.

Therefore this should only return ReactComponent<any, any, any> | PI | null.

To ensure that you must call getPublicInstance on the stateNode for the appropriate tag.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments.

@iamdustan iamdustan force-pushed the fiber-test-renderer-refinements branch from 9d42ea0 to 41bdb14 Compare January 11, 2017 20:28
@iamdustan
Copy link
Contributor Author

Updated @sebmarkbage. Thanks!

@@ -170,7 +170,7 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T

deferredUpdates,

getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | I | TI | null) {
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | PI | null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function still needs to pass stateNode to getPublicInstance for the relevant tags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how could I reproduce this scenario? None of the current tests seem to have an issue. I attempted to reproduce it with a root ref and the return value of render with a few component types.

describe('root level refs', () => {
  it('attaches and detaches root refs in stack', () => {
    assertForRenderer(require('ReactDOM'));
  });

  it('attaches and detaches root refs in fiber', () => {
    assertForRenderer(require('ReactDOMFiber'));
  });

  const assertForRenderer = (DOM) => {
    spyOn(console, 'error');
    var ref = jest.fn();
    var container = document.createElement('div');
    var result = DOM.render(<div ref={ref} />, container);
    expect(ref).toHaveBeenCalledTimes(1);
    expect(ref.mock.calls[0][0]).toBeInstanceOf(HTMLDivElement);
    expect(result).toBe(ref.mock.calls[0][0]);
    DOM.unmountComponentAtNode(container);
    expect(ref).toHaveBeenCalledTimes(2);
    expect(ref.mock.calls[1][0]).toBe(null);

    class Comp extends React.Component {
      render() {
        return <div>Comp</div>;
      }
    }

    ref = jest.fn();
    result = DOM.render(<Comp ref={ref}/>, container);

    expect(ref).toHaveBeenCalledTimes(1);
    expect(ref.mock.calls[0][0]).toBeInstanceOf(Comp);
    expect(result).toBe(ref.mock.calls[0][0]);
    DOM.unmountComponentAtNode(container);
    expect(ref).toHaveBeenCalledTimes(2);
    expect(ref.mock.calls[1][0]).toBe(null);
  };
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think that getPublicRootInstance will ever call getPublicInstance. They both seem to be added for backwards compatibility with different features and operate independently.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, what should this be:

var result = ReactDOMFiber.render(5, container);
// result? '5' or fiber?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public API must never expose fibers.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this exists for Stack compat I would return nothing. Stack doesn't allow rendering top-level text nodes so nothing would break if we didn't implement this.

If we did want to implement it (I don't see why) then it would make sense to return the TextInstance (text node), similar to how ReactDOM.render(<div />) returns the Instance (div).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is “current” behavior. Even if I remove the getPublicInstance added from the ref, the result in that example is not '5' as I expected. I think, though, that that is a different issue..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I think about this the less I’m convinced that this should be changed from I | TI to PI. PI was only added for getPublicInstance which is only called via attachRef. The naming may be unfortunate since getHostPublicInstance and getPublicInstance aren’t related (internally).

Fwiw, I’m not attempting or desiring to implement anything new here, just trying to understand what the behavior was before #8628.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var instA;
var instB = MyRenderer.render(<div ref={n => instA = n} />);
expect(instA).toBe(instB);

Is that always true with the test renderer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah. That’d do it. Test case and possible solution implemented in 9a9a4db.

iamdustan added a commit to iamdustan/react that referenced this pull request Jan 13, 2017
@iamdustan iamdustan force-pushed the fiber-test-renderer-refinements branch from e6f2186 to 2a705c6 Compare January 13, 2017 12:57
iamdustan added a commit to iamdustan/react that referenced this pull request Jan 13, 2017
@iamdustan iamdustan force-pushed the fiber-test-renderer-refinements branch from 2a705c6 to f78245f Compare January 17, 2017 01:05
iamdustan added a commit to iamdustan/react that referenced this pull request Jan 17, 2017
@iamdustan iamdustan force-pushed the fiber-test-renderer-refinements branch from f78245f to 9a9a4db Compare January 17, 2017 01:06
iamdustan added a commit to iamdustan/react that referenced this pull request Jan 17, 2017
const root : FiberRoot = (container.stateNode : any);
const containerFiber = root.current;
if (!containerFiber.child) {
return null;
}
return containerFiber.child.stateNode;
return getPublicInstance(containerFiber.child.stateNode);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still suffers from this problem: https://github.com/facebook/react/pull/8628/files/9a9276b3fbcec4cfb6d5c69e891c5feafe0195f2#r95653360 Which remains a problem in commitWork.

You need to check whether containerFiber.child.tag === HostComponent before passing it to getPublicInstance.

Otherwise, in the case of render(<Foo />) you will pass an instance of Foo to getPublicInstance which is neither an I nor TI so the function doesn't know what to do with it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will become more clear after #8545 because then the stateNode will have a proper type and Flow would've disallowed passing it to getPublicInstance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/facebook/react/pull/8751/files#diff-7002876230f7effe9aa22e292f6a52c6R463 handles it in CommitWork. Added the switch for this method as well to do the same for getRootPublicInstance. Some tests have regressed that I’ll need to look into why.

Shouldn’t render(<Foo />) pass the I | TI back for backwards compatibility?

@iamdustan iamdustan force-pushed the fiber-test-renderer-refinements branch from 9a9a4db to 5156c65 Compare January 21, 2017 18:35
});

it('attaches and detaches root refs in fiber', () => {
assertForRenderer('ReactDOMFiber');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally we try to avoid code that is as dynamic as this. It makes it harder to trace what's going on.

@gaearon
Copy link
Collaborator

gaearon commented May 30, 2017

I think this might be ready for another look.

switch (containerFiber.child.tag) {
case HostComponent:
return getPublicInstance(containerFiber.child.stateNode);
default:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case should match on ClassComponent only. It won't type check if we use proper disjoint unions on Fibers.

That type error would be correct since this can be other types such as fragments, coroutines etc. Those should do something reasonable like throw, drill down (like findDOMNode does) or return null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't type check if we use proper disjoint unions on Fibers.

Is that PR any closer to landing by chance?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll have to be a massive rebase. We could try it again though. There are some new features in Flow that can make that palatable now.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepting because it is no more buggy than what is already there but I left a comment.

@gaearon gaearon merged commit 80bdebc into facebook:master May 30, 2017
@gaearon
Copy link
Collaborator

gaearon commented May 30, 2017

OK. I merged and added followup as a child of existing todo in #7925.

@iamdustan iamdustan deleted the fiber-test-renderer-refinements branch June 1, 2017 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants