@@ -102,76 +102,92 @@ long as the code adheres to core Redux principles:
102102
103103### Context  
104104
105- As long as you use the [ current Context
105+ We're on board with the current API where possible; there's a
106+ third-party library we use that isn't there yet.
107+ 
108+ #### Current Context API  
109+ 
110+ We should use the [ current Context
106111API] ( https://reactjs.org/docs/context.html )  instead of the [ legacy
107- one] ( https://reactjs.org/docs/legacy-context.html ) , there's not much
108- to worry about here. From the doc for the current one:
112+ one] ( https://reactjs.org/docs/legacy-context.html )  wherever possible.
113+ The new API aggressively ensures consumers will be updated
114+ (re-` render ` ed) on context changes, and the old one doesn't (see
115+ below). From the [ new API's
116+ doc] ( https://reactjs.org/docs/context.html ) :
109117
110118>  All consumers that are descendants of a Provider will re-render
111119>  whenever the Provider’s ` value `  prop changes.
112120
113- Then, anticipating the question "What about ` shouldComponentUpdate ` ?"
114- (and therefore, "What about ` PureComponent ` s?", because
115- ` PureComponents ` 
116- [ implement] ( https://reactjs.org/docs/react-api.html#reactpurecomponent ) 
117- ` shouldComponentUpdate ` ):
121+ It's so aggressive that there's a potential "gotcha" with the new API:
122+ context consumers are the first occurrence of the following that we're
123+ aware of (from the [ doc on
124+ ` shouldComponentUpdate ` ] ( https://reactjs.org/docs/react-component.html#shouldcomponentupdate ) ):
125+ 
126+ >  In the future React may treat ` shouldComponentUpdate() `  as a hint
127+ >  rather than a strict directive, and returning ` false `  may still
128+ >  result in a re-rendering of the component.
129+ 
130+ We gather this from the following (in the [ new API's
131+ doc] ( https://reactjs.org/docs/context.html ) ):
118132
119133>  The propagation from Provider to its descendant consumers (including
120134>  [ ` .contextType ` ] ( https://reactjs.org/docs/context.html#classcontexttype ) 
121- >  and
122- >  [ ` useContext ` ] ( https://reactjs.org/docs/hooks-reference.html#usecontext ) )
123- >  is not subject to the shouldComponentUpdate method, so the consumer is
124- >  updated even when an ancestor component skips an update.
125- 
126- (I think word "even" means to emphasize "ancestor" -- i.e., it's not
127- only the case that a consumer's * own*  ` shouldComponentUpdate `  is
128- ignored, but that, remarkably, so is the ` shouldComponentUpdate `  of
129- ancestor components. I think it's just as remarkable that a
130- component's own ` shouldComponentUpdate `  is ignored: this sounds like a
131- clear instance of the
132- [ following] ( https://reactjs.org/docs/react-component.html#shouldcomponentupdate ) :
133- "In the future React may treat ` shouldComponentUpdate() `  as a hint
134- rather than a strict directive, and returning ` false `  may still result
135- in a re-rendering of the component.")
135+ >  [ ...] )
136+ >  is not subject to the shouldComponentUpdate method
137+ 
138+ Concretely, this means that our ` MessageList `  component updates
139+ (re-` render ` s) when the theme changes, since it's a ` ThemeContext ` 
140+ consumer, * even though its ` shouldComponentUpdate `  always returns
141+ ` false ` * . So far, this hasn't been a problem because the UI doesn't
142+ allow changing the theme while a ` MessageList `  is in the navigation
143+ stack. If it were possible, it would be a concern: setting a short
144+ interval to automatically toggle the theme, we see that the message
145+ list's color scheme changes as we'd want it to, but we also see the
146+ effects that ` shouldComponentUpdate `  returning ` false `  is meant to
147+ prevent: losing the scroll position, mainly (but also, we expect,
148+ discarding the image cache, etc.).
149+ 
150+ #### Legacy Context API  
136151
137152The legacy Context API is
138153[ declared] ( https://reactjs.org/docs/legacy-context.html#updating-context ) 
139- fundamentally broken because ` shouldComponentUpdate `  and
140- ` PureComponent ` s can block updates.
141- 
142- We're forced to reckon with the legacy Context API in one place: the
143- ` react-intl `  library uses it for the ` intl `  context. We "translate"
144- the old API to the new one ASAP, in ` TranslationContextTranslator ` , so
145- we can get used to using Context in the new way. But we've made
146- ` TranslationContextTranslator `  a ` PureComponent ` , exposing ourselves
147- to the legacy API's flaw. So we have to use a "` key `  hack", which is
148- the following:
149- 
150- If ` locale `  changes, we make the entire React component tree at and
151- below ` IntlProvider `  remount. (Not merely re-` render ` : completely
152- vanish and start over with ` componentDidMount ` ; see the note at [ this
153- doc] ( https://5d4b5feba32acd0008d0df98--reactjs.netlify.app/docs/reconciliation.html ) 
154- starting with "Keys should be stable, predictable, and unique".) We do
155- this with the [ ` key ` 
156- attribute] ( https://reactjs.org/docs/lists-and-keys.html ) , which isn't
157- recommended for use except in lists.
158- 
159- Previously, we used the same ` key `  hack for our own styles data, to
160- get changes in the theme to propagate. No longer! :)
154+ fundamentally broken because consumers could be blocked from receiving
155+ updates to the context, and not just by the consumer's own
156+ ` shouldComponentUpdate ` :
157+ 
158+ >  The problem is, if a context value provided by component changes,
159+ >  descendants that use that value won’t update if an intermediate
160+ >  parent returns ` false `  from ` shouldComponentUpdate ` . This is totally
161+ >  out of control of the components using context, so there’s basically
162+ >  no way to reliably update the context.
163+ 
164+ We have to think about the legacy Context API in just one place. The
165+ ` react-intl `  library's ` IntlProvider `  uses it to provide the ` intl ` 
166+ context. The only consumer of ` intl `  is
167+ ` TranslationContextTranslator ` , which "speaks" the old API by being
168+ the direct child of ` IntlProvider `  and by being a ` Component ` , not a
169+ ` PureComponent `  (whose under-the-hood ` shouldComponentUpdate `  would
170+ suppress updates on context changes)—all to make sure that it
171+ re-` render ` s whenever ` intl `  changes. Then,
172+ ` TranslationContextTranslator `  is itself a provider, and it provides
173+ the same value, but it does so in the new Context API way. All its
174+ consumers are updated appropriately, which is what we want.
161175
162176### The exception: ` MessageList `   
163177
164- We have one React component that we wrote (beyond ` connect `  calls) that 
165- deviates from the above design: ` MessageList ` .  This is the only 
166- component that extends plain  ` Component `  rather than ` PureComponent ` , and
167- the only component that implements  ` shouldComponentUpdate ` .
178+ We have one React component that we wrote (beyond ` connect `  calls)
179+ that  deviates from the above design: ` MessageList ` .  It extends plain 
180+ ` Component `  rather than ` PureComponent ` , and it's the only component 
181+ in which we implement  ` shouldComponentUpdate ` .
168182
169183In fact, ` MessageList `  does adhere to the Pure Component Principle -- its
170184` render `  method is a pure function of ` this.props `  and ` this.context ` .  So
171185it could use ` PureComponent ` , but it doesn't -- instead we have a
172186` shouldComponentUpdate `  that always returns ` false ` , so even when ` props ` 
173187change quite materially (e.g., a new Zulip message arrives which should be
174- displayed) we don't have React re-render the component.
188+ displayed) we don't have React re-render the component. (See the note
189+ on the current Context API, above, for a known case where our
190+ ` shouldComponentUpdate `  is ignored.)
175191
176192The specifics of why not, and what we do instead, deserve an architecture
177193doc of their own.  In brief: ` render `  returns a single React element, a
0 commit comments