Skip to content

Add support for createRef() and react-create-ref#1138

Merged
marvinhagemeister merged 8 commits into
masterfrom
createref-support
Aug 14, 2018
Merged

Add support for createRef() and react-create-ref#1138
marvinhagemeister merged 8 commits into
masterfrom
createref-support

Conversation

@developit

@developit developit commented Jun 9, 2018

Copy link
Copy Markdown
Member

Like the title says, this adds support for Object refs - the createRef() API (returns an empty object). It should also work with the react-create-ref package.

Worth noting, this is around a 38 byte size increase and puts us up to 3535b, which I'm not thrilled about.


It's possible we could punt support for createRef() into preact-compat, since it can be implemented atop function refs quite easily:

function createRef() {
  return function ref(c) { ref.current = c; }
}

... however that would not be compatible with the react-create-ref package, so if we got that route we'll need to provide an alias for it that points to preact-compat's createRef.

@coveralls

coveralls commented Jun 9, 2018

Copy link
Copy Markdown

Coverage Status

Coverage remained the same at 100.0% when pulling 9cb51ed on createref-support into fe76b79 on master.

Comment thread src/preact.js Outdated
import { rerender } from './render-queue';
import options from './options';

const createRef = Object;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just curious: Is using Object faster than a literal {}?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

slower. I just benchmarked and while I'd originally thought it'd be worth using Object here, the performance hit is surprising (50%):
https://esbench.com/bench/5b1d4021f2949800a0f61d25

@marvinhagemeister

marvinhagemeister commented Jun 10, 2018

Copy link
Copy Markdown
Member

Had to read up on the backstory a bit on why react made an the API change. It's basically because of code like this:

class Foo extends Component {
  frames = [];

  render() {
    // <div> is passed a new function on each render()
    return <div ref={el => {this.frames.push(el)}} />
  }
}

Overall, it's great to see more work being put into removing wrong usages of an API, but I must admit that I'm a bit worried that this will be the first change of many others where we "memoize" function creation.

Spoke a bit with Dan at JSConf about this and the render props pattern which has a similar issue. The React team is currently exploring alternative ideas and would like to make them unnecessary at some point in the future. There are no short term plans for that, though. It just something they are currently exploring.

I wouldn't worry too much about supporting react-create-ref. It's only a temporary polyfill to ease migration to react v16.3+. Once migrated, there is no use for react-create-ref anymore.

@developit Does your second implementation work as well as the changes in this PR? Just tried it out and it would be 5 bytes smaller.

@developit

developit commented Jun 10, 2018

Copy link
Copy Markdown
Member Author

@marvinhagemeister it works pretty similarly. I'm actually seeing a performance improvement with this implementation though, so I might try to benchmark between the two for comparison.

The alternative version is technically different from how React implements createRef (since it returns a function with a .current property), but the outward API is the same.

@marvinhagemeister

Copy link
Copy Markdown
Member

@developit Right, thanks for getting back to me. In that case I think we should not deviate too much from react.

@marvinhagemeister

Copy link
Copy Markdown
Member

ping @lukeed

@developit

Copy link
Copy Markdown
Member Author

Are we thinking the 38 byte hit is okay?

@lukeed

lukeed commented Jun 14, 2018

Copy link
Copy Markdown
Member

Sorry for the delay, crazy week.

The only shavings (lol) I can suggest is typeof ref == 'function' into !!ref.call.

Other than that, I think I'm of the mindset that we should become react-16 compatible all at once, when that point comes, rather than piecemeal some 16 attributes. Will be harder for users to keep track of what we currently do / don't do while still intentionally being incomplete.

Comment thread src/util.js
*/
export function applyRef(ref, value) {
if (ref!=null) {
if (typeof ref=='function') ref(value);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This line is here to be compatible with the current way refs work, right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yep

@developit

Copy link
Copy Markdown
Member Author

I think I tried the ref.call bit but it ended up producing a larger output since we already do typeof checks in the codebase but don't currently have any .call.

@alexparish

alexparish commented Aug 7, 2018

Copy link
Copy Markdown

Other than that, I think I'm of the mindset that we should become react-16 compatible all at once, when that point comes, rather than piecemeal some 16 attributes. Will be harder for users to keep track of what we currently do / don't do while still intentionally being incomplete.

I tend to disagree. Lack of support for react-16 features could be holding back adoption of Preact due to incompatibility with libraries (eg. reach/router#9). Rolling out support as soon as a feature is ready could help adoption. For example, I'm super keen to use Preact in production but am held back through issues like this.

@marvinhagemeister

Copy link
Copy Markdown
Member

Merging as everybody is happy with the change 🎉

@marvinhagemeister marvinhagemeister merged commit 8aa7ec9 into master Aug 14, 2018
developit added a commit to preactjs/preact-compat that referenced this pull request Aug 16, 2018
Implemented in preactjs/preact#1138, we just have to export it here.
This won't do anything until Preact 8.4.0 is released.
@GordonCode25

Copy link
Copy Markdown

Sorry in advance if this is a dumb question.
Is there a way to get createRef to work with preact.min.js? Like is there a version of that file floating around somewhere that has react.createRef() support?

@marvinhagemeister

Copy link
Copy Markdown
Member

@gordygordy No, we haven't made a new release yet. If absolutely need the latest unreleased code from master (note that it may be unstable), you can clone the repo and just run npm run build to create the same files as we ship on npm.

@GordonCode25

Copy link
Copy Markdown

I think I solved it, i just pasted this into the bottom of the preact.min.js file and now everything works :)

function createRef() {return function ref(c) { ref.current = c; }}

@developit

Copy link
Copy Markdown
Member Author

@gordygordy I'd recommend avoiding modifying anything in node_modules if possible, it'll get reset when you install stuff.

@MathieuLoutre

Copy link
Copy Markdown

Can the react-create-ref package be used with preact in the meantime, waiting for the next release?

@marvinhagemeister

Copy link
Copy Markdown
Member

@MathieuLoutre should work right out of the box with react aliased to preact

@MathieuLoutre

Copy link
Copy Markdown

I've tried it and it seems to work brilliantly. Just wanted to make sure that it was supposed to do that ;) Thank you!

ngyikp added a commit to ngyikp/preact that referenced this pull request Dec 10, 2018
`ceeateRef` was added in preactjs#1138
however the Flow definition wasn’t added, so using it on Flow
codebases was quite inconvenient
@developit developit deleted the createref-support branch March 4, 2019 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants