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

Commit

Permalink
Notify components in hierarchical order
Browse files Browse the repository at this point in the history
  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.
  • Loading branch information
dk00 committed Dec 9, 2016
1 parent 189f3f0 commit fa3690e
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 23 deletions.
32 changes: 13 additions & 19 deletions src/linking.ls
Original file line number Diff line number Diff line change
Expand Up @@ -9,55 +9,49 @@ function flat-diff a, b
function name => (it && (it.display-name || it.name || 'no name')) || \none

function onChange listeners, update
level = listeners
..add update
level.delete.bind level, update
listeners.add update
listeners.delete.bind listeners, update

function notify listeners
Array.from listeners.keys! .map (update) ->
update! if listeners.has update
!function notify listeners
Array.from listeners.keys! .map (update) -> update!

function select-next select, props, hold
function handle-change select, props
if flat-diff @selected, next = select @store.getState!, props
@selected = next
@changed = true
@setState empty unless hold
true
else notify @source.listeners

function chain create-store, select, merge, render
hooks = if create-store
if create-store
componentWillMount: ->
listeners = new Set
@resolve = notify.bind void listeners
@store = create-store @resolve
@source = {@store, listeners}
getChildContext: -> @source
else
componentWillMount: ->
@store = @context.store
@selected = select @store.getState!, @props
@source = listeners: new Set
componentDidMount: ->
@off = onChange @context.listeners, ~>
select-next.call @, select, @props
@setState empty if handle-change.call @, select, @props
componentWillUnmount: -> @off!
<<<
display-name: "linking #{name render}: " + [select, merge]map name
render: ->
@changed = false
render merge @selected, @store.dispatch, @props

if select?length > 1
hooks.componentWillReceiveProps = ->
select-next.call @, select, it, true
hooks.shouldComponentUpdate = -> @changed
hooks
getChildContext: -> @source
componentWillReceiveProps: -> handle-change.call @, select, it
shouldComponentUpdate: -> @changed

function link {createElement: h}: React
do
that = React.PropTypes.any
all = store: that, listeners: that
origin = childContextTypes: all
sub = contextTypes: all
sub = contextTypes: all, childContextTypes: listeners: that

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

Expand Down
27 changes: 23 additions & 4 deletions test/linking.ls
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ function cut-select t
function child
count++
h \div
linked = link child, ({count}) -> value: ((count || 0) + 1)%2
lower = link child, ({count}) -> value: ((count || 0) + 1)%2

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

sample-render linked .then -> t.equal count, 2 desc

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

function ordered-notify t
desc = 'notify higher hierarchy components prior to lower'

sequence = []
expected = 'higher lower 'repeat 4 .trim!
function child => h \div
function select which => ->
sequence.push which
value: (it.count <? 3) + which

lower = link child, select \lower
higher = link -> h \div,, lower!
, select \higher

sample-render higher .then ->
t.equal (sequence.join ' '), expected, desc

function test t
cases = [add-context, pass-state, listen-changes, prop-changes
unmount-unsubscribe, cut-select]
unmount-unsubscribe, cut-select, ordered-notify]

cases.reduce (previous, run) -> previous.then -> run t
, Promise.resolve!
Expand Down

0 comments on commit fa3690e

Please sign in to comment.