Create a gist now

Instantly share code, notes, and snippets.

@amcdnl /Actions.js
Last active May 18, 2017

What would you like to do?
import { action } from 'common/utils/redux/redux';
@action
export class Actions {
static types = {
set3d: Symbol('set3d')
};
static set3d(val) {
return {
type: Actions.types.set3d,
val
};
}
}
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { configureStore } from 'common/utils/redux/redux';
import { Viz } from './Viz';
import { Reducer } from './Reducer';
const store = configureStore(Reducer, {
options: {
layout: {
type: 'radialTree',
is3d: false
}
},
model: {}
});
export class Container extends Component {
render() {
return (
<Provider store={store}>
<Viz />
</Provider>
)
}
}
// THIS ISNT REALLY RELEVANT BUT WANTED TO DISCLOSE ANYWAY
// http://raganwald.com/2015/06/26/decorators-in-es7.html
export function mixin (behaviour) {
const staticKeys = Reflect.ownKeys(behaviour.prototype ? behaviour : {});
const instanceKeys = Reflect.ownKeys(behaviour.prototype || behaviour);
const typeTag = Symbol('isa');
function _mixin (clazz) {
for (let property of staticKeys) {
Object.defineProperty(clazz, property, {
value: behaviour[property],
writable: true,
static: true
});
}
for (let property of instanceKeys) {
Object.defineProperty(clazz.prototype, property, {
value: behaviour[property],
writable: true
});
}
Object.defineProperty(clazz.prototype, typeTag, { value: true });
return clazz;
}
Object.defineProperty(_mixin, Symbol.hasInstance, {
value: (i) => !!i[typeTag]
});
return _mixin;
}
import { Actions } from './Actions';
import { reduce } from 'common/utils/redux/redux';
@reduce
export class Reducer {
static [Actions.types.set3d](state, { val }) {
state.options.layout.is3d = val;
return state;
}
}
import { bindActionCreators as reduxBinder, applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import { mixin } from '../decorators/mixin';
export const action = mixin(class {
static actors (clazz) {
return Object.keys(clazz.types).reduce((newObj, k) => {
newObj[k] = clazz[k];
return newObj;
}, {});
}
});
export function bindActionCreators(act, dispatch) {
return reduxBinder(act.actors(act), dispatch);
};
export function configureStore(reducers, initialState = {}) {
return createStore(
reducers.invoke(),
initialState,
applyMiddleware(thunk)
);
}
export const reduce = mixin(class {
static invoke (initalState = {}) {
return (state = initalState, action) => {
const handler = this[action.type];
return handler ?
handler(state, action) :
state;
};
}
});
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'common/utils/redux/redux';
import { Actions } from './Actions';
import { VizMenu } from './vizMenu/VizMenu';
@connect(state => state)
export class Viz extends Component {
static propTypes = {
model: PropTypes.object,
options: PropTypes.object
};
// componentWillMount() {
// this.unsubscribe = store.subscribe(() => {
// return this.updateState();
// });
// }
// componentWillUnmount() {
// this.unsubscribe();
// }
// updateState() {
// this.setState(store.getState());
// }
render() {
const { dispatch, options, model } = this.props;
const actions = bindActionCreators(Actions, dispatch);
return (
<div>
<VizMenu
options={options}
{...actions}
/>
<Graph
options={options}
model={model}
/>
</div>
)
}
}
import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';
import './vizNav.css!';
export class VizMenu extends Component {
static propTypes = {
options: PropTypes.object
};
help(){
alert('meow')
}
render() {
const { set3d, options } = this.props;
const { is3d, type } = options.layout;
const className = classNames({ 'active': is3d }) + 'btn btn-link';
const buttonTxt = is3d ? '3d' : '2d';
return (
<nav className='viz-menu'>
<ul>
<li>
<button
type="button"
className={className}
onClick={ () => set3d(!is3d) }>
{buttonTxt}
</button>
</li>
<li>
{type}
</li>
<li>
<button
className='btn btn-link'
type="button"
onClick={ () => this.help() }>
Help
</button>
</li>
</ul>
</nav>
)
}
}

gaearon commented Feb 24, 2016

This:

  static [Actions.types.set3d](state, { val }) {
    state.options.layout.is3d = val;
    return state;
  }

is a mutation. In Redux, mutations are not allowed. Please see http://redux.js.org/docs/Troubleshooting.html for details.

Owner

amcdnl commented Feb 24, 2016

so something like this?

  static [Actions.types.set3d](state, { val }) {
    let clone = Object.assign({}, state);
    clone.options.layout.is3d = val;
    return clone;
  }

still did not work :S

gaearon commented Feb 24, 2016

No, that wouldn’t work because Object.assign() is shallow. You need to copy everything that changes, deep mutations are not allowed either. You probably want this:

  static [Actions.types.set3d](state, { val }) {
    return Object.assign({}, state, {
      options: Object.assign({}, state.options,
        layout: Object.assign({}, state.options.layout,
          is3d: val
        })
      })
    });
  }

This looks terribly verbose but it is correct. We can make it better with the spread operator proposal (not part of ES6 but relatively low-risk future syntax):

  static [Actions.types.set3d](state, { val }) {
    return {
      ...state,
      options: {
        ...state.options,
        layout: {
          ...state.options.layout,
          is3d: val
        }
      }
    };
  }

This looks better but still a bit hard to read. What do we do now? Reducer composition! There is a reason we define reducers as regular functions in the official tutorials: to make it clear they can call each other.

import { combineReducers } from 'redux'

function layout(state = { is3d: false }, action) {
  switch (action.type) {
  case Actions.types.set3d:
    return { ...state, is3d: true }
  default:
    return state
  }
}

const options = combineReducers({
  layout,
  /* other state.options.* reducers */
})

const rootReducer = combineReducers({
  options,
  /* other state.* reducers */
})

Now it looks pretty, is easy to follow, and different state branches are decoupled. :-)

Owner

amcdnl commented Feb 24, 2016

YOU ARE AMAZING!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment