Props vs States vs Stores

In notebook:
FrontEndMasters React
Created at:
2016-06-16
Updated:
2016-06-17
Tags:
libraries React JavaScript
Shows a decision tree diagram: https://github.com/FrontendMasters/2015-02-13-React/blob/master/props-v-state.png

Prop: if I don't need to mutate the value (somebody else passes it to me)

Store (another module): If me and someone unrelated to me (some other part of the app) needs to change the value (use store) And everybody else in the app asks that module and send messages to change the state of the store in the module
Also use a store if you need to persist the change when remounted. For example a sidebar: users opens it, moves to another page. If you want the sidebar state to persist then use a store. 

State: if no-one else is concerned by this data (no other modules, child or parent element) than you can use the state 
- Is localStorage similar to store?
- yes, localStorage could be a store. Store can be any kind of object that can hold on to a state. localStorage, server, firebase, whatever

- elaborate on mounting and unmounting?
- code example:
  ..
render: function() {
    if (something) {
        return <div><Sidebar/></div>;
    } else {
        return <div/>;
    }
}
if ​something == true​ -> mounted
if ​something == false​ -> unmounted

So if you need to persist the state after mounting and unmounting and re-mounting then you need a store.
Moves to directory 4-props-v-state

Imagine if your app is getting bigger, and you want two ​contentToggle​ elements on your page (from the previous examples). 
  ..

var App = React.createClass({
    toggleAll () {
        console.log('uhh ... now what?');
    },
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    <ContentToggle summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>

                    <ContentToggle summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
How can implement ​toggleAll​ (opens all)? 

One route would be to a new attribute to ​ContentToggle​:
<ContentToggle isOpen={this.state.toggleAll} summary="Jerk Chicken">​ :
  ..

// 4. ++++ need to change ContentToggle as well
var ContentToggle = React.createClass({
    getInitialState: function () {
        return {
            // 5. ++++ Change this to this.props.isOpen
            showDetails: this.props.isOpen
        }
    }
    
})


..

var App = React.createClass({
    // 2. ++++ Add the state element
    getInitialSate () {
        return {
            toggleAll: true // sets it to true after false didn't work
        }
        
    },
    
    toggleAll () {
        // 3. ++++ change this, set the state
        this.setState({
            // set it to the opposite
            toggleAll: !this.state.toggleAll;
        });
    },
    
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    // 1. ++++ add isOpen
                    <ContentToggle isOpen={this.state.toggleAll} summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>

                    <ContentToggle isOpen={this.state.toggleAll} summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
Now the Toggle All button opens all items, but doesn't toggle. Need to add more code.
NOTE: all this is an anti-pattern (to demonstrate the complexity)
  ..

var ContentToggle = React.createClass({
    getInitialState: function () {
        return {
            showDetails: this.props.isOpen
        }
    },
    
    // 1. ++++ need to add another React life-cycle hook:
    // inspect props before the component will receive it 
    //  and not only in getInitialState
    componentWillReceiveProps  (newProps) {
        if (newProps.isOpen) {
            this.setState({showDetails: newProps.showDetails.isOpen });
        }
        
    },
    ..    
})

..

var App = React.createClass({
    getInitialSate () {
        return {
            toggleAll: true
        }
        
    },
    
    toggleAll () {
        this.setState({
            toggleAll: !this.state.toggleAll;
        });
    },
    
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    <ContentToggle isOpen={this.state.toggleAll} summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>

                    <ContentToggle isOpen={this.state.toggleAll} summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
​componentWillReceiveProps​ - when the component remounts and will receive new props, I can inspect them.
So far, we only read these ​props​ in ​getInitialState​.
Tests the app, and it still does not work reliably. The root of the problem is that the children components should synchronise back their state to the parent who also manages the open/close.

Back to adding more spaghetti:
  ..

var ContentToggle = React.createClass({
    getInitialState: function () {
        return {
            showDetails: this.props.isOpen,
            // 1. ++++ add more states
            toggleStates: {
                jerk: true,
                thai: true
            }
        }
    },
    
    componentWillReceiveProps  (newProps) {
        if (newProps.isOpen) {
            this.setState({showDetails: newProps.showDetails.isOpen });
        }
        
    },
    ..    
})

..

var App = React.createClass({
    getInitialSate () {
        return {
            toggleAll: true
        }
        
    },
    
    toggleAll () {
        this.setState({
            toggleAll: !this.state.toggleAll;
        });
    },
    
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    // 2. ++-- change `this.state.toggleAll`
                    <ContentToggle isOpen={this.state.toggleStates.jerk} summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>
                    
                    // 2. ++-- change `this.state.toggleAll`
                    <ContentToggle isOpen={this.state.toggleStates.thai} summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
Thankfully Ryan gives up...

Again, the problem is that children and parent elements want to manage each other['s state]. You would need a two-way binding.

A better strategy would be to have ​ContentToggle​ be pure component.
  • given the same input always give the same output
  • don't give any side effects
Pure components don't have their own state. You just send in the props and they will always behave the same way. 
In the case ​ContentToggle​ you need "someone else" from the outside to control it.