Skip to content

Basic Example

shaunpersad edited this page Aug 26, 2017 · 3 revisions

The following is a simple color-picker, where we display a list of colors, and allow a single color to be selected.

In this example, we must pass down both an application-level state property (selectedColor), as well as an action function (changeColor), all the way from the ColorList top-level component to the ColorOptions grand-child components:

The "vanilla" React version

// app.js, the "entrypoint" of the app.
import ColorList from './components/ColorList';

ReactDOM.render(
    React.createElement(ColorList),
    document.getElementById('root')
);
// ColorList.jsx, displays the list of colors
import Color from './Color';

class ColorList extends React.Component {

    constructor(props) {
        super(props);
        this.state = { selectedColor: props.colors[0] };
        this.changeColor = this.changeColor.bind(this);
    }

    changeColor(color) {
        this.setState({selectedColor: color});
    }

    render() {
        return (
            <div>
                <p>
                    The selected color is {this.state.selectedColor}
                </p>
                <div>
                    {this.props.colors.map(color =>
                        <Color
                            key={color}
                            color={color}
                            changeColor={this.changeColor}
                            selectedColor={this.state.selectedColor}
                        />
                    )}
                </div>
            </div>
        );
    }
    
    static get defaultProps() {
        return {
            colors: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
        };
    }
}

export default ColorList;
// Color.jsx, each list item
import ColorOptions from './ColorOptions';

function Color(props) {

    return (
        <div>
            <span>This color is {props.color}</span>
            <ColorOptions
                color={props.color}
                changeColor={props.changeColor}
                selectedColor={props.selectedColor}
            />
        </div>
    );
}

export default Color;
// ColorOptions.jsx, displays the relevant options for a particular color.
function ColorOptions(props) {
    return (
        <div>
            <span>Replace {props.selectedColor} with {props.color}?</span>
            <button onClick={e => props.changeColor(props.color)}>yes</button>
        </div>
    );
}

export default ColorOptions;

The Redu version

Let's "redu" it. Our goal will be to eliminate the number of props that we need to pass down from the ColorList to the ColorOptions components.

To accomplish this, we will move all of the shared application-level state and action functions out of the ColorList component and into the ColorListStore. We will then subscribe the ColorList and the ColorOptions to the ColorListStore in order to derive what we need from it.

// app.js, the "entrypoint" of the app.
import ColorListStore from './stores/ColorListStore';

ReactDOM.render(
    React.createElement(ColorListStore),
    document.getElementById('root')
);
// ColorListStore.js, creates the application-level store.
import { createStore } from 'redu';
import ColorList from '../components/ColorList';

const ColorListStore = createStore(ColorList);

ColorListStore.defaultProps = {
    colors: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
};

ColorListStore.initialState = {
    selectedColor: ColorListStore.defaultProps.colors[0]
};

ColorListStore.actions = {
    changeColor(color) {
        this.setState({
            selectedColor: color
        });
    }
};

export default ColorListStore;
// ColorList.jsx, displays the list of colors
import { createSubscriber } from 'redu';
import Color from './Color';

function ColorList(props) {

    return (
        <div>
            <p>
                The selected color is {props.selectedColor}
            </p>
            <div>
                {props.colors.map(color =>
                    <Color key={color} color={color} />
                )}
            </div>
        </div>
    );
}

export default createSubscriber(ColorList, (colorListStoreState, colorListStoreProps) => {

    return {
        selectedColor: colorListStoreState.selectedColor,
        colors: colorListStoreProps.colors
    };
});
// Color.jsx, each list item
import ColorOptions from './ColorOptions';

function Color(props) {

    return (
        <div>
            <span>This color is {props.color}</span>
            <ColorOptions color={props.color} />
        </div>
    );
}

export default Color;
// ColorOptions.jsx, displays the relevant options for a particular color.
import { createSubscriber } from 'redu';

function ColorOptions(props) {
    return (
        <div>
            <span>Replace {props.selectedColor} with {props.color}?</span>
            <button onClick={e => props.changeColor(props.color)}>yes</button>
        </div>
    );
}

export default createSubscriber(ColorOptions, (colorListStoreState, colorListStoreProps, colorListStoreActions) => {

    return {
        selectedColor: colorListStoreState.selectedColor,
        changeColor: colorListStoreActions.changeColor
    };
});

Notice that we've solved problem #1 by eliminating threading props down multiple levels, and solved problem #2 via the ColorListStore.js, which, when viewed, gives a very good sense of what this app does.

And of course, problem #3 is solved as well, since we had no need to create action creators or reducers.


Next: Other Examples and Concepts