Snippets

Edwin Marshall Mode Props Before and After

Created by Edwin Marshall last modified
/* The original problem
 * We needed a way to specify props which were only available in certain modes. To do so manually, we would call connect and manually do some 
 * merging:
 */

import React from "react";
import {connect} from "react-redux";
import {Mode} from "app/reducers/app";

const Input = (props) => <input {...props} />

export default connect((state, props) => {
    const mode = state.app.get("mode", Mode.Display);
    return R.merge(props, R.propOr({}, mode, props.modeProps));
})(Input);

/* 
 * In the above, we are saying to combine the original props with those found in modeProps for the particular mode. This gets tedious when you
 * want to mess with more than modes, as you need to make sure futher prop manipulations act on the new mode-specific props, rather than the 
 * original.
 */
/* connectWithMode to the rescue...I thought
 * The idea here was that if we had a version of connect that automatically merged in mode-specific props,
 * we could save a lot of boilerplate and clean up some things.
 */
 
import React from "react";
import {connectWithMode} from "app/utils/redux";
import {Mode} from "app/reducers/app";

const Input = (props) => <input {...props} />

export default connectWithMode()(Input);

/*
 * While this is less boiler-platey, it comes at the cost of not being able to do further modifications of props that require access to state.
 * You see, mode-specific props aren't available until *after* connectWithMode returns. Thus, this function will receive all the original props,  
 * not those that resulted from taking mode into consideration:
 */
const mapStateToProps = (state, props) => {
    // If this function is passed to connectWithMode, the props referenced will be the original props, not the ones transformed by modeProps
};

/*
 * As such, you have to do prop manipulation in a life cycle method or manually construct mode-specific props within mapStateToProps, which is
 * both redundant and undermines the purpose of having connectWithMode.
 */
/* In the future
 * What we really want is the access to mode-specific props that the original provides, with less boilerplate, but without the restrictions 
 * imposed by connectWithMode
 */
 
import React from "react";
import {connect} from "react-redux";
import {modeProps} from "app/utils/redux";
import R from "ramda";

const Input = (props) => <input {...props} />

export default connect((state, props) => {
    const appState = state.app.toJS();
    // the location of mode changed recently
    const {mode} = state.components;
    const newProps = modeProps(mode, props);
    
    return newProps;
})(Input);

// If we need to do further manipulation, doing so is now trivial
const mapStateToProps = (state, props) => {
    const appState = state.app.toJS();
    const {mode} = state.components;
    const newProps = modeProps(mode, props);
    const {source, inputKey} = newProps;
    
    return R.merge(newProps, {
        sourceValue: R.path(source, appState),
        inputValue: R.path(inputKey, appState)
    });
};

/*
 * While destructuring props and state may seem boilerplatey, a few things should be noted: First, I intend to eventually get rid of
 * Immutable.js, which will make the `state.app.toJS()` line go away and allow me to instead say `const {app} = state`. Second, once
 * state is a plain old JavaScript object, we could pass them inline if we wanted to instead of destructuring
 */

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.