Skip to content
This repository was archived by the owner on Apr 27, 2020. It is now read-only.

Commit fa3690e

Browse files
committed
Notify components in hierarchical order
Components used to subscribe to store updates in componentDidMount, and it runs from lowest hierarchy. A child could subscribe earlier and receive update earlier than its parent, but it might recieve new props, or be removed after update of its parent. Changes are made to fix inconsistencies, by notifying components in hierarchical order. All components that subscribe to store updates now create a new listener collection for its children, and notify after update. This fixes reduxjs/react-redux#292 . Updates to components are initiated by calling top level listeners, and these components notify their children after handling changes. reduxjs/react-redux#398 is also fixed by send only necessary notifications: When a component don't have to update in response to state changes, its children is notified by calling the listeners. When a component need to update, its listeners are not called, use simply setState(empty), and let React handle the rest.
1 parent 189f3f0 commit fa3690e

File tree

2 files changed

+36
-23
lines changed

2 files changed

+36
-23
lines changed

src/linking.ls

+13-19
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,49 @@ function flat-diff a, b
99
function name => (it && (it.display-name || it.name || 'no name')) || \none
1010

1111
function onChange listeners, update
12-
level = listeners
13-
..add update
14-
level.delete.bind level, update
12+
listeners.add update
13+
listeners.delete.bind listeners, update
1514

16-
function notify listeners
17-
Array.from listeners.keys! .map (update) ->
18-
update! if listeners.has update
15+
!function notify listeners
16+
Array.from listeners.keys! .map (update) -> update!
1917

20-
function select-next select, props, hold
18+
function handle-change select, props
2119
if flat-diff @selected, next = select @store.getState!, props
2220
@selected = next
2321
@changed = true
24-
@setState empty unless hold
25-
true
22+
else notify @source.listeners
2623

2724
function chain create-store, select, merge, render
28-
hooks = if create-store
25+
if create-store
2926
componentWillMount: ->
3027
listeners = new Set
3128
@resolve = notify.bind void listeners
3229
@store = create-store @resolve
3330
@source = {@store, listeners}
34-
getChildContext: -> @source
3531
else
3632
componentWillMount: ->
3733
@store = @context.store
3834
@selected = select @store.getState!, @props
35+
@source = listeners: new Set
3936
componentDidMount: ->
4037
@off = onChange @context.listeners, ~>
41-
select-next.call @, select, @props
38+
@setState empty if handle-change.call @, select, @props
4239
componentWillUnmount: -> @off!
4340
<<<
4441
display-name: "linking #{name render}: " + [select, merge]map name
4542
render: ->
4643
@changed = false
4744
render merge @selected, @store.dispatch, @props
48-
49-
if select?length > 1
50-
hooks.componentWillReceiveProps = ->
51-
select-next.call @, select, it, true
52-
hooks.shouldComponentUpdate = -> @changed
53-
hooks
45+
getChildContext: -> @source
46+
componentWillReceiveProps: -> handle-change.call @, select, it
47+
shouldComponentUpdate: -> @changed
5448

5549
function link {createElement: h}: React
5650
do
5751
that = React.PropTypes.any
5852
all = store: that, listeners: that
5953
origin = childContextTypes: all
60-
sub = contextTypes: all
54+
sub = contextTypes: all, childContextTypes: listeners: that
6155

6256
render, select, merge=default-merge, create-store, options <- (wrap =)
6357

test/linking.ls

+23-4
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,12 @@ function cut-select t
108108
function child
109109
count++
110110
h \div
111-
linked = link child, ({count}) -> value: ((count || 0) + 1)%2
111+
lower = link child, ({count}) -> value: ((count || 0) + 1)%2
112112

113-
sample-render linked .then ->
114-
t.equal count, 2 desc
113+
function upper => h \div,, lower!
114+
linked = link upper, ({count}) -> value: count + 2
115+
116+
sample-render linked .then -> t.equal count, 2 desc
115117

116118
function unmount-unsubscribe t
117119
desc = 'stop notifying unmounted components'
@@ -129,9 +131,26 @@ function unmount-unsubscribe t
129131
sample-render linked .then ->
130132
t.equal last-value, 3 desc
131133

134+
function ordered-notify t
135+
desc = 'notify higher hierarchy components prior to lower'
136+
137+
sequence = []
138+
expected = 'higher lower 'repeat 4 .trim!
139+
function child => h \div
140+
function select which => ->
141+
sequence.push which
142+
value: (it.count <? 3) + which
143+
144+
lower = link child, select \lower
145+
higher = link -> h \div,, lower!
146+
, select \higher
147+
148+
sample-render higher .then ->
149+
t.equal (sequence.join ' '), expected, desc
150+
132151
function test t
133152
cases = [add-context, pass-state, listen-changes, prop-changes
134-
unmount-unsubscribe, cut-select]
153+
unmount-unsubscribe, cut-select, ordered-notify]
135154

136155
cases.reduce (previous, run) -> previous.then -> run t
137156
, Promise.resolve!

0 commit comments

Comments
 (0)