Skip to content
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

Warn for functional refs on stateless functional components #7272

Closed

Conversation

aweary
Copy link
Contributor

@aweary aweary commented Jul 13, 2016

Resolves #7267

Currently React warns if you do:

<StatelessComponent ref='foo' />

But it doesn't warn if you do

<StatelessComponent ref={node => { /* ... */ }} />

It seems like a generally good idea to have a consistent warning when using refs with any SFC.

var instance = component.getPublicInstance();
warning(
instance !== null,
'Stateless function components cannot have refs.'
Copy link

Choose a reason for hiding this comment

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

Maybe keep it consistent with the docs and name it "Stateless functional components"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This warning is consistent with the current invariant in ReactCompositeComponent: https://github.com/facebook/react/blob/master/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#L1182

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

Yes, knowing which component has the invalid ref would help a lot.

@satya164
Copy link

What if I pass a function from parent and use it for the ref? String refs won't work. But I think function refs do.

@aweary
Copy link
Contributor Author

aweary commented Jul 13, 2016

@satya164 do you mean like passing a function down to a child via the ref property so it can be used to attach a ref to a child component that isn't stateless?

If it's a stateless component it will always pass null to whatever function is provided in the ref prop, so it wouldn't actually provide any benefit, even if the passed function is bound to a stateful parent.

@satya164
Copy link

@aweary Ah.

@ghost ghost added the CLA Signed label Jul 14, 2016
'Stateless function components cannot be given refs ' +
'(See %s%s).',
componentName,
owner ? ' created by ' + owner.getName() : ''
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO this should follow the normal pattern check the render method of ...
Something like Stateless functional component %s cannot have refs attached. Check the render method of %s. I also think that the other message (when using a string ref) should follow this pattern too.

@edvinerikson
Copy link
Contributor

Someone from FB should probably review this too.
It looks pretty good but I think that we should have a invariant if we try to create a ref inside a stateless component too (this PR only warns if we try to put a ref on a stateless component).

function A() {
  return <B ref="B-ref" /> // invariant called
  return <B ref={() => {}} /> // invariant is not called
}

@aweary
Copy link
Contributor Author

aweary commented Jul 18, 2016

It looks pretty good but I think that we should have a invariant if we try to create a ref inside a stateless component too (this PR only warns if we try to put a ref on a stateless component).

@edvinerikson React should already warn for

return <B ref="B-ref" />

This PR actually implements what I think you're describing, if I'm understanding you correctly. If you look at the test, you can see what this PR is addressing:

return <StatelessComponent name="A" ref={() => {}}/>;

Currently, React will call the ref function with whatever is returned from getPublicInstance which is always null for these SFCs, this PR checks to see if that instance is null or not and throws if it is.

@ghost ghost added the CLA Signed label Jul 18, 2016
@edvinerikson
Copy link
Contributor

edvinerikson commented Jul 18, 2016

What I tried to explain is that if you use ref inside a stateless functional component it will not warn that you do that. However it warns that you have attached a ref to a stateless component.

class AClass extends Component {}

function FunctaionalComponent() {
  return <AClass ref={() => {}} />
// this won't warn right now but it should because using refs inside a stateless
// component shouldn't be possible, right? (Not possible with string refs at least)
}

Edit:
Using a function as ref is probably a valid use case

@aweary
Copy link
Contributor Author

aweary commented Jul 18, 2016

@edvinerikson thanks for clarifying, that should be doable by checking the instance for owner as well.

@ghost ghost added the CLA Signed label Jul 18, 2016
@aweary
Copy link
Contributor Author

aweary commented Jul 18, 2016

It's currently failing on a couple tests using ReactTestUtils, such as ReactTestUtils-test.js#L215-L232 as the component being passed to attachRef is returning null for getPublicInstance. So I'll have to address that

It seems like ReactTestUtils eventually just returns an instance of NoopInternalComponent which will always return null for getPublicInstance, so this warning is going incorrectly fire when using the shallow renderer.

@edvinerikson
Copy link
Contributor

It's currently failing on a couple tests using ReactTestUtils, such as ReactTestUtils-test.js#L215-L232 as the component being passed to attachRef is returning null for getPublicInstance. So I'll have to address that

I guess that maybe you can check if component._currentElement.type is a function, might be hacky though

@aweary
Copy link
Contributor Author

aweary commented Jul 18, 2016

I guess that maybe you can check if component._currentElement.type is a function, might be hacky though

Yeah, I'd hate to do that just because of ReactTestUtils. I wonder if we can consider actually returning an instance for ReactShallowRenderer, at least in some cases. Maybe createRenderer could accept a configuration object that lets users enable this. I'm not sure if that would break anything though.

@edvinerikson
Copy link
Contributor

Someone from FB will probably need to decide what to do.
cc @zpao

@ghost ghost added the CLA Signed label Jul 21, 2016
@gaearon
Copy link
Collaborator

gaearon commented Nov 20, 2016

@edvinerikson

// this won't warn right now but it should because using refs inside a stateless
// component shouldn't be possible, right?

It is possible for callback refs.

class AClass extends Component {}

function FunctaionalComponent() {
  var inst;
  return <AClass ref={a => inst = a} />
}

This is why callback refs are better than string refs.

@gaearon gaearon self-assigned this Nov 20, 2016
@gaearon
Copy link
Collaborator

gaearon commented Nov 20, 2016

@aweary Would you like to revive this?

@aweary aweary force-pushed the warn-ref-funcs-stateless-components branch from 0ade166 to 75f9efe Compare November 20, 2016 20:58
@aweary
Copy link
Contributor Author

aweary commented Nov 20, 2016

@gaearon rebased. There are a few failing tests for ReactTestRenderer and ReactTestUtils that I'll look at soon 👍

@gaearon
Copy link
Collaborator

gaearon commented Jan 9, 2017

Ping :-)

@gaearon
Copy link
Collaborator

gaearon commented Jan 9, 2017

Actually I think this is superseded by #8635 (which also adds them for Fiber).
Thanks for your work though!

@gaearon gaearon closed this Jan 9, 2017
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.

5 participants