Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

memoize state reading #12105

Closed
wants to merge 8 commits into from
Closed

Conversation

petemill
Copy link
Member

@petemill petemill commented Nov 27, 2017

Fix #11515

The reactjs/reselect library is a common method to memoize the result of functions that read or compute data based on state. It creates functions that are only re-computed when the relevant state changes. It becomes useful when:

  1. A state tree is large
  2. When portions of the state tree are modified often, and not all of the state tree is modified on every action dispatch
  3. When immutablejs is used to represent state, and therefore partial tree equality comparison oldstate.get('portion1') === newstate.get('portion2') can be used very effectively.

Problems this PR aims to solve

  • Time spent recalculating react/redux component instance mergeProps functions
    • A high number of components exist that subscribe to state changes
    • A high number of actions are dispatched that change certain relevant state properties
    • A high number of time is spent re-computing state changes where dependent state properties have not changed
    • Many components run the same expensive state-parsing functions within the same render-cycle but re-run those functions nevertheless.

This PR adds the following features:

  • Common state accessors that are not dynamic, and should only be re-evaluated when a portion of the state tree changes are turned in to static memoized functions (e.g. in xxxState.js)
    • Identify and address some main ones
    • Create Issues for refactoring some others
  • Component instance-specific state accessors that are computed from dynamic properties, e.g. the state of a specific tab, frame, etc, are memoized per-instance use-case
    • Allow mergeProps to return a function or instance-specific memoized selector
    • Consider re-reselect to cache common dynamic selectors (e.g. frames or tabs)
  • Some components are passed data as ownProps, when a child's state is very closely tied to its immediate parent, instead of calculating the same props again, and will benefit from its parent's mergeProp memoization
  • Enhances equality comparison for appState / windowState by only merging the two when neccessary

This PR results in the following metric improvements:
...

Submitter Checklist:

  • Submitted a ticket for my issue if one did not already exist.
  • Used Github auto-closing keywords in the commit message.
  • Added/updated tests for this change (for new code or code which already has tests).
  • Ran git rebase -i to squash commits (if needed).
  • Tagged reviewers and labelled the pull request as needed.

Test Plan:

Reviewer Checklist:

Tests

  • Adequate test coverage exists to prevent regressions
  • Tests should be independent and work correctly when run individually or as a suite ref
  • New files have MPL2 license header

@codecov-io
Copy link

codecov-io commented Nov 27, 2017

Codecov Report

Merging #12105 into master will increase coverage by 0.07%.
The diff coverage is 64.08%.

@@            Coverage Diff             @@
##           master   #12105      +/-   ##
==========================================
+ Coverage   56.27%   56.34%   +0.07%     
==========================================
  Files         282      282              
  Lines       28027    28086      +59     
  Branches     4584     4594      +10     
==========================================
+ Hits        15772    15825      +53     
- Misses      12255    12261       +6
Flag Coverage Δ
#unittest 56.34% <64.08%> (+0.07%) ⬆️
Impacted Files Coverage Δ
app/common/state/notificationBarState.js 52% <40%> (-48%) ⬇️
app/renderer/components/reduxComponent.js 60.9% <63.41%> (-0.31%) ⬇️
js/state/frameStateUtil.js 67.17% <67.16%> (+6.01%) ⬆️
app/common/state/windowState.js 91.83% <75%> (-0.42%) ⬇️
app/common/state/siteSettingsState.js 59.09% <90%> (+1.94%) ⬆️
js/stores/appStoreRenderer.js 91.66% <0%> (-8.34%) ⬇️
js/stores/windowStore.js 27.65% <0%> (-0.3%) ⬇️
... and 1 more

@petemill petemill force-pushed the perf/memoize-state-reading branch from 3114644 to 6c0cc18 Compare January 25, 2018 19:14
@petemill petemill added this to the 0.21.x (Beta Channel) milestone Feb 15, 2018
@petemill petemill force-pushed the perf/memoize-state-reading branch from 6c0cc18 to 6267450 Compare February 15, 2018 04:34
@bsclifton bsclifton modified the milestones: 0.21.x (Beta Channel), 0.23.x (Nightly Channel) Feb 17, 2018
petemill and others added 4 commits February 19, 2018 14:52
This ensures equality comparison will work as expected when no state changes, or windowState changes and appState does not.
Improves performance by only recalculating if appropriate state tree has changed
@petemill petemill force-pushed the perf/memoize-state-reading branch from 6267450 to 08303b5 Compare February 19, 2018 22:54
… cache instance-specific selectors

This helps keep state-lookup and calculation caches based on frame or tab, whilst managing the cache size by way of when components get created and destroyed.
…'wasted' time per Component, as well as the existing total time
@bsclifton bsclifton modified the milestones: 0.23.x (Nightly Channel), 0.22.x (Developer Channel) Feb 20, 2018
@bbondy bbondy modified the milestones: 0.22.x (Developer Channel), 0.23.x (Nightly Channel) Feb 25, 2018
@alexwykoff
Copy link
Contributor

closing in favor of other priorities

@alexwykoff alexwykoff closed this Jun 19, 2018
@bsclifton bsclifton removed this from the 0.24.x (Developer Channel) milestone Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

component mergeProps are unnecessarily running
5 participants