Skip to content

Instantly share code, notes, and snippets.

@danprince
Last active August 24, 2016 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danprince/2ea3dc641f76baedb6a6181a368b655f to your computer and use it in GitHub Desktop.
Save danprince/2ea3dc641f76baedb6a6181a368b655f to your computer and use it in GitHub Desktop.
Redux, Aphrodite & Zaphod

Concerns

React Dev Tools

Any wrapped components will be rendered into the React dev tools with their wrappers intact. This becomes pretty invasive when most of your components are using a HOC wrapper.

Here's a workaround for defining HOCs that respect stateless functional components and don't render a wrapper.

function withName(Component) {
  class EnhancedComponent extends React.Component {
    render() {
      return Component(this.props, this.context);
    }
  }
  
  EnhancedComponent.displayName = Component.displayName || Component.name;
}

This way, the actual <Component> doesn't end up in the React tree. However, it only works for stateless components.

Shallow Rendering

Shallow rendering doesn't work with components that are wrapped by HOC components.

shallow(<div><h1>Hi</h1></div>)
// => <div><h1>Hi</h1></div>

let Counter = connect(Counter)
shallow(Counter)
// => <Connected />

To keep components testable without enforcing the container/presentational divide, wrapped components need to be squashed and kept stateless.

import { createComponent } from 'raz';
import { update } from 'zaphod';
// Styles, Actions and State are elevated to first-class citizens
function render({ props, styles, actions, state }) {
return (
<div className={state >= 0 ? styles.pos : styles.neg}>
<button onClick={actions.dec}>-</button>
<span>{state}</span>
<button onClick={actions.inc}>+</button>
</div>
);
}
// Works like react-redux mapStateToProps, except it maps it
// to the first-class state object instead.
function withState(state) {
return state.count;
}
// Works like react-redux mapDispatchToProps, except action
// creators are autobound with dispatch, before being passed.
function withActions() {
return {
inc(n=1) {
return { type: 'inc', amount: n };
},
dec(n=1) {
return { type: 'dec', amount: n };
}
}
}
// Components with a reducer only have local state
function withReducer(state={count:0}, action) {
switch(action.type) {
case 'inc':
return state::update('count', n => n + action.amount);
case 'dec':
return state::update('count', n => n - action.amount);
default:
return state;
}
}
// Aphrodite turns this into a stylesheet and uses it to
// generate classNames ready for use in render.
function withStyles() {
return {
pos: { color: 'green' },
neg: { color: 'red' }
};
}
export default createComponent({
render, withStyles, withState, withActions, withReducer
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment