react.js – interest calculator with React+Redux


problem statement: using React and Redux build an interest calculator that, given a principal, rate of annual interest, and number of years, will display the total principal plus interest using the formula
TOTAL = principal * (1 + (rate * years))

Any feedback welcome on the style, approach, etc.

<!doctype html>
<html><body>
<section></section>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/redux"></script>
<script src="https://unpkg.com/react-redux"></script>
<script src="https://unpkg.com/htm"></script>
<script>
const jsx = htm.bind(React.createElement);

function reducer(model={
    total: 0,
}, action={type:'', payload: null}){
    let store;
    switch(action.type){
    case 'TOTAL_COST':
        store = { total: action.payload.total };
    break;
    default:
        store = model;
    }
    return store;
}
const actions = {
    updateTotal: function({ principal, years, rate }){
        return {
            type: 'TOTAL_COST',
            payload: {total: principal * (1 + (rate * years))}
        }
    }
};
const store = Redux.createStore(reducer);

class App extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            principal: 1000,
            years: 7,
            rate: 0.025
        };
        this.submit = this.submit.bind(this);
        this.update = this.update.bind(this);
        this.submit(new CustomEvent('init'));
    }
    update(e){
        //const val = e.target.value * 1;
        let { valueAsNumber, value, name } = e.target;
        const state = {...this.state};
        if(isNaN(valueAsNumber)){
            valueAsNumber = 0;
        }
        state( name ) = valueAsNumber;
        this.props.dispatch( actions.updateTotal(state) );
        this.setState({(name): valueAsNumber});
    }
    submit(e){
        e.preventDefault();
        this.props.dispatch( actions.updateTotal(this.state) );
    }
    //TOTAL = principal * (1 + (rate * years))
    render(){
        const { principal, years, rate } = this.state;
        return jsx`<form onSubmit=${ this.submit }>
<style>
label{display:block;}
</style>
<h2>interest calculator</h2>
<h1><label> total <span>${ this.props.total.toFixed(2) }</span> </label></h1>
<fieldset>
<label> principal <input name=principal onInput=${ this.update } type="number" min=1 defaultValue=${ principal } /> </label>
<label> years <input name=years onInput=${ this.update }type="number" min=1 max=300 defaultValue=${ years } /> </label>
<label> rate <input name=rate onInput=${ this.update } type="number" min=0 max=100 defaultValue=${ rate } step=0.0001 /> </label>
</fieldset>
 <button>calculate</button>
</form>`;
    }
}

const ConnectedApp = ReactRedux.connect((store)=>{return {total:store.total}})(App);

ReactDOM.render(
    jsx`<${ReactRedux.Provider} store=${ store }> <${ ConnectedApp } key=${ Date.now() }> <//> <//>`,
    document.querySelector('section')
);

requestAnimationFrame(()=>{
    console.log(`🚀 tests running → any failures will be shown below`);
    requestAnimationFrame(()=>{ console.log(`🍻 tests done → any failures are shown above`); });

    const { updateTotal } = actions;
    let result = updateTotal({principal: 5000, years: 5, rate: 0.025}).payload;
    let expected = 5625;
    console.assert(Math.round(result.total) === expected, `expected total ${ result.total } to be ${ expected }`, result);

});
</script>