Replacing State with Props

In notebook:
FrontEndMasters React
Created at:
2016-06-17
Updated:
2016-06-17
Tags:
libraries React JavaScript
So first we need to reset some stuff. 
  ..

var ContentToggle = React.createClass({
    getInitialState: function () {
        return {
            showDetails: false
        }
    },
    
    ..    
})

..

var App = React.createClass({
    getInitialSate () {
        return {
            toggleAll: true
        }
        
    },
    
    toggleAll () {
        console.log('uh what now')
    },
    
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    <ContentToggle isOpen={this.state.toggleStates.jerk} summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>
                    
                    <ContentToggle isOpen={this.state.toggleStates.thai} summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
Continue from here
  ..

var ContentToggle = React.createClass({
    // 2. ---- Can remove the state 
    // ---- getInitialState: function () {
    // ----     return {
    // ----         showDetails: false
    // ----     }
    // ---- },
    
    // 3. ++++ add props:
    propTypes : {
        isOpen: React.PropTypes.bool
    },

    ..    
    
    renderDetails () {
        var showStuff = this.state.showDetails;
        if (showStuff) {
            return this.props.children;
        } else {
            return null;
        }
    },
    
    toggle () {
        // still uses state, will need to be changed later
        this.setState({ 
            showDetails: !this.state.showDetails 
        });
    }
    
    render () {
        var summaryClassName = "ContentToggle__Summary";
        // 4. ++-- change if(this.state.showDetails)
        // use props instead of states
        if(this.props.isOpen){
            details = this.props.children;
            summaryClassName += " ContentToggle__Summary--open";
        }
        
        return (
            <div className="ContentToggle">
                <div onClick={this.toggle} className="summaryClassName">{this.props.summary}</div>
                <div className="ContentToggle__Details">{this.renderDetails()}</div>
            </div>
        );
    }
});
    
})

..

var App = React.createClass({
    getInitialSate () {
        return {
            toggleAll: true,
            // 1. ++++
            toggleSates: {
                jerk: true,
                thai: false
            }
        }
        
    },
    
    toggleAll () {
        console.log('uh what now')
    },
    
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    <ContentToggle isOpen={this.state.toggleStates.jerk} summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>
                    
                    <ContentToggle isOpen={this.state.toggleStates.thai} summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
Now, on the first run the app works according to the settings in ​getInitialState​ 

But now the individual components click handlers don't seem to work. need to change the ​toggle​ handler above:
      
    // 2. ++++ add proptype:
    propTypes : {
        isOpen: React.PropTypes.bool.isRequired,
        onToggle: React.PropTypes.func.isRequired
    },
    
    toggle () {
        // 1. ++-- change
        this.props.onToggle();
    },
    
    //  3. ++++ update maybefocus to use props
    maybeFocus () {
        if (this.props.isOpen) {
            this.refs.details.getDOMNode().focus();
        }
    },
Normally all state has been removed. But still need to update ​ContentToggle​ attributes
  <ContentToggle 
    // 1. ++++ add onToggle
    onToggle={this.handleToggle.bind(this, 'jerk');
    
    isOpen={this.state.toggleStates.jerk} summary="Jerk Chicken">
    <p>It was delicious</p>
</ContentToggle>
Does the same for the other ​ContentToggle​. 
Add ​handlToggle​ (just above the ​render ()​)
  handleToggle(id) {
    console.log(id);
}
So now, the parent owns the state. 
  ..

var ContentToggle = React.createClass({

    propTypes : {
        isOpen: React.PropTypes.bool.isRequired,
        onToggle: React.PropTypes.func.isRequired
    },
    
    toggle () {
        this.props.onToggle();
    },
    
    maybeFocus () {
        if (this.props.isOpen) {
            this.refs.details.getDOMNode().focus();
        }
    },
    ..    
    
    renderDetails () {
        var showStuff = this.state.showDetails;
        if (showStuff) {
            return this.props.children;
        } else {
            return null;
        }
    },
    
    toggle () {
        this.setState({ 
            showDetails: !this.state.showDetails 
        });
    },
    
    handleToggle(id) {
        // 1. ++++ set the handler
        var { toggleStates } = this.state;
        // set to the opposite
        toggleStates[id] = !toggleStates[id];
        this.setState({ toggleStates })
        // same as { toggleStates: toggleStates }
    }
    
    render () {
        var summaryClassName = "ContentToggle__Summary";
        if(this.props.isOpen){
            details = this.props.children;
            summaryClassName += " ContentToggle__Summary--open";
        }
        
        return (
            <div className="ContentToggle">
                <div onClick={this.toggle} className="summaryClassName">{this.props.summary}</div>
                <div className="ContentToggle__Details">{this.renderDetails()}</div>
            </div>
        );
    }
});
    
})

..

var App = React.createClass({
    getInitialSate () {
        return {
            toggleAll: true,
            toggleSates: {
                jerk: true,
                thai: false
            }
        }
        
    },
    
    toggleAll () {
        console.log('uh what now')
    },
    
    render () {
        return (
            <div>
                <h1>Props v. State</h1>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div style={{margin: '10px 0'}}>
                    <ContentToggle 
                        onToggle={this.handleToggle.bind(this, 'jerk');
                        isOpen={this.state.toggleStates.jerk} summary="Jerk Chicken">
                        <p>It was delicious</p>
                    </ContentToggle>
                    
                    <ContentToggle 
                        onToggle={this.handleToggle.bind(this, 'thai');
                        isOpen={this.state.toggleStates.thai} summary="Thai">
                        <p>It was probably good too</p>
                    </ContentToggle>
                </div>
                
            </div>    
        )
    }
});
You push your state upwards in the application. 

The cursor pattern. When you move your state all the way to the top – the cursor. This is the OM pattern in ClojureScript. 
David Nolan  ClojureScript library - uses React with ClojureScript. Use one big unmutable object. If it changes, the whole app re-renders.  
CircleCI - shows a video. Can have snapshots of states. 

The state moves upwards and your app is made from pure components.

Another pattern is to go all the way to the top, then to the site in stores.(Flux pattern)
The above code works, but the focus (tab focus) doesn't move to the content.
Need to add componentDidUpdate life cycle method (component re-renders and updates):
      toggle () { .. },
    
    componentDidUpdate () {
        this.maybeFocus();
    }
    
    maybeFocus: function { .. },
    
    handleToggle(id) {.. }
Now the focus moves to the child