-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Use React.createContext() by @cellog #1000
Conversation
I just ran another perf benchmark for 5.0.7, #995, and #1000 , set to 2500 connected components. Here's the FPS (avg / min-max) and Chrome trace time split results for each one (30-second test runs):
The implementation of #1000 is conceptually interesting, and I like its relative simplicity (just memoize in render vs splitting across two components), but based on these numbers it seems noticeably slower. Can anyone else confirm the numbers? I did fresh builds from both PR branches (including tweaking the Rollup config to set |
can you link to the perf script/setup you're using again? |
needs a pushed commit with your changes though @markerikson |
I pushed the sourcemap usage changes to the perf repo. For building React-Redux, it's just adding I'll throw the average calculation into the perf runner. It's probably not entirely correct, as the FPS value changes could be happening at different times, and so the "average" should really be calculated using frame timestamps or something, but it's at least an objective comparison. |
The latest version of this PR is now published to npm under the npm install react-redux@next-1000 |
Oh, and renamed this and #995 so they pair up well in the PR list and browser's tab bar. |
I just pushed an update to this branch to use |
Progress Update - Ready for Beta-ish?I think I've got this branch not just cleaned up, but actually improved quite a bit. In particular, I've managed to simplify the logic around I think this is probably ready for a beta release of some kind so we can start getting real-world feedback. Actually, one interesting side note. I'd originally said we ought to require React 16.6 as a minimum baseline, on the grounds that I was planning to use Performance TechniquesThe big improvement in these latest commits was due to bringing back a trick from v4: memoizing the actual element reference returned from if (!haveMergedPropsChanged && renderedElement) {
return renderedElement
}
if (withRef) {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
ref: 'wrappedInstance'
})
} else {
this.renderedElement = createElement(WrappedComponent,
this.mergedProps
)
}
return this.renderedElement In v5, that kind of went away, since we do all the memoization work up front, and only call In v6, we're back to doing more work in I remembered the memoized element trick while playing with the hooks-ified branch I posted over in #1065 , and was able to reuse that with a handwritten memoization function here. Performance BenchmarksI've updated the benchmarks repo over at https://github.com/reduxjs/react-redux-benchmarks , and ran a new set of benchmarks with version 5.0.7, an earlier commit from this branch (4855c84, which I think was before Tim rebased it), and my new commit 2d06cdf . The repo currently has three benchmark apps:
They're artificial stress tests, but they are at least some kind of objective numbers that we can look at. Each test is run for 30 seconds twice in Puppeteer. The first time tries to capture FPS results, the second activates a Chrome trace to capture scripting/rendering/painting times. In theory, anyone ought to be able to clone the benchmarks repo, build from this branch, drop the UMD build into And now, drumroll please, the results:
|
OK, ran things through Prettier (I'll get on top of #1071 after this) and we should be good to go. |
Yay! |
It should warn if store is passed in props because we don't support that any more, and explain how to do it by passing in a context in the error message. Check the original tests from my PR to see how I did it if you want to copy |
* alternate 6.x implementation * use canary values in store instead of setting a prop on the store * remove createProvider (missed this one before) * fix renderCountProp, add a test also clean up unused canary values * remove errant console.log * expose context consumer and provider This will allow third party apps to use these in their code * changes requested by @timdorr * export Context instead of just Consumer/Provider * fix error messages for removed functionality * minor displayName change * keep prop-types in production minified UMD build when the time is right, one need only change the BABEL_ENV for build:umd:min back to "rollup-production" * performance optimizations: HEADS UP API change too * React.forwardRef is VERY slow, on the order of 2x slower in our benchmark. So we only enable it if withRef="forwardRef" folks using withRef=true will get an error telling them to update and not rely on getWrappedInstance() but just to use the ref directly * renderCountProp is removed, as this is natively supported in React dev tools now * all usages of shallowEquals are removed for pure components, it was unnecessary. * instead of allowing passing in a custom Context consumer in props, it is now required to be passed in via connect options at declaration time. * small optimizations/refactors * fix storeKey error, allow unstable_observedBits * update hoist-non-react-statics * cosmetics * Replace `withRef="fowardRef"` option with `forwardRef=true` * Less package-lock.json noise * Bump React dep to 16.6, and remove shallow-equals * Switch context variable and prop naming, and use the whole object * Update Provider implementation and use storeState field * Rework Provider tests * Rework connect tests, combine warnings, and remove observedBits Ported connect test handling from 995 Deduped the "custom store context" messages Removed use of observedBits for now Commented out derivedProps propTypes that caused test failures * Rework connect component based on review notes Removed use of PureWrapper Used React.memo() on the wrapped component Renamed and extracted makeDerivedPropsSelector Added makeChildElementSelector Simplified render props callback Simplified forwardRef handling * Fix tests around custom context * Fix custom context as a prop usage * Fix lint warnings * Run through Prettier. Update lockfile.
Supersedes #997, based off of master so no merge conflicts
takes into account @timdorr 's request regarding Provider.spec.js not using canary values
to see the commit history that led to this, check out #997.
This fixes
#914 (implements forwardRef and works with forwardRef element as connected component too)
#943 (removes prop-types in production build)
#802 (no try/catch block - mSP is called directly from render() and is bubbled up naturally)
and addresses
#950 (implements React context)
#890 (uses async-safe subscription to redux in Provider)
This PR combines an alternate approach to implementing Context to #995 and also the move to react-testing-library (#996) because testing React context with enzyme isn't yet possible. The PR does the following:
React.createContext()
React.forwardRef
<Provider>
and context consumer to connected components:getDerivedStateFromProps
, instead, derived props are memoized inrender()
componentDidMount
andcomponentDidUpdate
of theProvider
component, subscriptions are not handled at all in connected components, except to read from React's context consumer. This doesn't address tearing and other async issues that are caused by redux being synchronous in nature. Fixing this will require breaking redux's backwards compatibility, and that's a different discussion.Provider
and handled by React context.Drawbacks to this approach:
withRef
is gone for connected components. The code errors out explaining that the user should simply pass a ref in and it will work. This will cause codebases to break that are relying on withRef. Fortunately, the fix is to remove withRef and update code to use standard reference handling procedures in React. docs need to be updatedstore
cannot be passed as a prop to connected components. The code errors if a user attempts to pass store to a connected component. The error explains to use a customProvider
(as noted above) to fix this, but will cause breaks in the few programs using this feature. docs need to be updatedstoreKey
is irrelevant inconnect()
options. The code errors if a user attempts to pass this option inconnect()
's options. The error explains to simply use a nestedProvider
or a customProvider
. docs need to be updatedConnect
, the Context consumer, andPureComponent
wrapper around the component in pure mode (2 in impure mode). This is what makes the simplicity possible. The top-most component retrieves the context containing redux state, calculates derived props, and passes them to the inner component. Rather than try to do all of this beforeshouldComponentUpdate
, which leads to tons of complexity and brittle code, we pass it to the underlying component. sCU inPureComponent
works perfectly for our needs, preventing update if the derivedProps are unchanged.react-is
, which does not yet have an esm build, so we gain a little heft in exchange for the reduction in codeProvider.spec.js
@markerikson @timdorr