Skip to content

Commit

Permalink
0.3.3:
Browse files Browse the repository at this point in the history
- SubtreeMixin changes
- Support dynamic mounting and unmounting
- Add `subtreeReducer` field
- Refactor internals
  • Loading branch information
wcjohnson committed Feb 27, 2017
1 parent afda6f5 commit 80ac065
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 19 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# 0.3

## 0.3.3

### SubtreeMixin changes

The new mounting system enabled a cleanup of the `SubtreeMixin` internals, including the removal of the `didMount` monkey patch. This should not be a breaking change for those following the API, but may break code relying on subtree internals.

- `SubtreeMixin` components now support dynamic mounting. (Meaning that the `SubtreeMixin` component can be dynamically mounted, not that its contents can change dynamically -- please use `redux-components-map` if you need that functionality.)

- A new documented field, `this.subtreeReducer`, exists on all `SubtreeMixin` component instances. This is the reducer obtained by `combineReducers` over the subcomponents. Using this functionality, it is now possible to write custom reducers for `SubtreeMixin` components that fallback on the combined reducer when they don't understand an action.

- The `__reducerMap` and `__originalDidMount` undocumented fields no longer exist.

### Miscellaneous

- The `Object.assign` polyfill has been removed. Your runtime must support `Object.assign` natively, or you must polyfill it yourself.

## 0.3.2

### Observable selector changes
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# redux-components
A component model for Redux state trees based on the React.js component model and other familiar design patterns from the React ecosystem.

## What's New

- We've been making a lot of great improvements. Check out the [Change Log](CHANGELOG.md) for details -- particularly if you are moving to a higher significant version digit!

- Check out [redux-components-map](https://github.com/wcjohnson/redux-components-map) for a solution to a common use case: dynamic tree structure changes while your app is running.

## Documentation
> **NB:** redux-components is a tool that interoperates with [Redux](http://redux.js.org/). This documentation presumes a solid grasp of the fundamentals found in the [Redux docs](http://redux.js.org) -- particularly the concepts of the Redux state tree, reducers, action creators, and selectors.
Expand Down
37 changes: 19 additions & 18 deletions src/subtree.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { combineReducers } from 'redux'
import invariant from 'invariant'
import ReduxComponent from './ReduxComponent'
import createClass from './createClass'

import { willMountComponent, didMountComponent, willUnmountComponent } from './mountComponent'

##### SubtreeMixin
attachComponent = (parentComponent, key, component) ->
Expand All @@ -12,36 +12,37 @@ attachComponent = (parentComponent, key, component) ->
parentComponent[key] = component
childPath = parentComponent.path.concat( [ key ] )
component.__willMount(parentComponent.store, childPath, parentComponent)
# Return the reducer
component.reducer
component

applyDescriptor = (parentComponent, key, descriptor) ->
attachComponent(parentComponent, key, createComponent(descriptor))

export SubtreeMixin = {
getReducer: -> @subtreeReducer

componentWillMount: ->
# Sanity check that our component supports subtrees
if process.env.NODE_ENV isnt 'production'
invariant(typeof @getSubtree is 'function', "redux-component of type #{@displayName} (mounted at location #{@path}) is using SubtreeMixin, but does not have a getSubtree() method.")

# Get the subtree structure
subtree = @getSubtree()
# Conjure child components and gather their reducers
__reducerMap = {}
for key, descriptor of subtree
__reducerMap[key] = applyDescriptor(@, key, descriptor)
# Create composite reducer for parent component
reducer = combineReducers(__reducerMap)
@getReducer = -> reducer
# Create subcomponents
@__subtree = {}
for key, descriptor of @getSubtree()
@__subtree[key] = applyDescriptor(@, key, descriptor)

# Create reducer
reducerMap = {}
for key, component of @__subtree
reducerMap[key] = component.reducer
@subtreeReducer = combineReducers(reducerMap)

# Monkey-patch didMount to call subtree didMounts in the right order.
myDidMount = @__originalDidMount = @componentDidMount
@componentDidMount = ->
@[k]?.__didMount() for k of __reducerMap
myDidMount?.call(@)
componentDidMount: ->
didMountComponent(component) for key, component of @__subtree
undefined

componentWillUnmount: ->
@componentDidMount = @__originalDidMount
willUnmountComponent(component) for key, component of @__subtree
undefined
}

##### createComponent
Expand Down
15 changes: 14 additions & 1 deletion src/test/02-subtree.test.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{ inspect } = require 'util'

{ expect, assert } = require 'chai'
{ createClass, mountRootComponent, createComponent, SubtreeMixin } = require '..'
{ createClass, mountRootComponent, createComponent, SubtreeMixin, willUnmountComponent } = require '..'
{ makeAStore } = require './helpers/store'

describe 'subtree: ', ->
Expand All @@ -20,6 +20,8 @@ describe 'subtree: ', ->
console.log "Subcomponent.willMount"
componentDidMount: ->
console.log "Subcomponent.didMount"
componentWillUnmount: ->
console.log "Subcomponent.willUnmount"
getReducer: -> (state = {}, action) ->
switch action.type
when @SET then action.payload or {}
Expand Down Expand Up @@ -50,7 +52,15 @@ describe 'subtree: ', ->
componentWillMount: ->
console.log "RootComponent.willMount"
componentDidMount: ->
assert(@foo.isMounted())
assert(@bar.isMounted())
assert(@deep.isMounted())
console.log "RootComponent.didMount"
componentWillUnmount: ->
assert(not @foo.isMounted())
assert(not @bar.isMounted())
assert(not @deep.isMounted())
console.log "RootComponent.willUnmount"
getSubtree: -> {
foo: new Subcomponent()
bar: Subcomponent
Expand Down Expand Up @@ -86,3 +96,6 @@ describe 'subtree: ', ->
expect(rootComponentInstance.foo.getValue()).to.equal('foo')
expect(rootComponentInstance.bar.getValue()).to.equal('bar')
expect(rootComponentInstance.deep.zazz.getValue()).to.equal('deep.zazz')

it 'should unmount correctly', ->
willUnmountComponent(rootComponentInstance)

0 comments on commit 80ac065

Please sign in to comment.