Monthly Archives: June 2018

Why is there white space above the header?

I set no margin or padding at all, but there is white space above the header.

<header style="background-color:gray">
  <h1>Test margin collapsing</h1>
</header>	
 

There is no margin in the "header" or "h1". So why?

First of all, the browser adds margin to "h1". Open your developer console, you can see things like "-webkit-margin-before: 0.67em;"

So there should be space between "h1" and its parent, the "header". But how come the margin shows on top of the header?

That’s because of "margin collapsing". As said on MDN website, "If there is no border, padding, inline part, block formatting context created, or clearance to separate the margin-top of a block from the margin-top of its first child block, then those margins collapse". And what’s more surprising, " The collapsed margin ends up outside the parent. "

How to fix it? Just add a padding to the parent:

    <header  style="background-color: gray; padding: 1px">
		<h1 >Test margin collapsing</h1>
	</header>
 
 

Ajax in Redux with Thunk – Can’t be more weird

How can a ajax call fit into Redux paradigm?

  • State: The state will be updated after ajax call is over. No problem.
  • Reducer: Reducer will change the state when ajax call is over. No problem.
  • Action: Action can be used to define "Ajax call over"

But where to put the logic of really doing an ajax call?

None of the 3. It should be a plain function, which will dispatch "Ajax call over" action when Ajax fetching is done. But since people are so obesessed with Redux’s paradigm, this ajax calling logic is defined as a SPECIAL ACTION

  • It normally doesn’t have an action type, and it is not handled by reducers
  • But it can be "dispatched"
  • It’s not a data structure any more, but a function itself.

But how can a action be a function? That’s where the middleware "Thunk" comes into, which allows you to write action creators that return a function instead of an action


Here is an example. Before you go into it, you must know normally

  • Ajax call success and Ajax call failure will be defined as different actions
  • "Ajax call started" is also an action, since it will change the state in terms of "showing loading spinner"
//register the middleware
const rootStore = createStore(reducer, 
		applyMiddleware(thunkMiddleware) //allows you to write action creators that return a function instead of an action 
);


//"real" actions and their reducers 
function counterReducer(state={value:0}, action){
    switch(action.type){
    	case 'getCounterStart': 
    		return {
    			...state, 
    			isFetching: true,  //used to indicate that the ajax request is running
    			error: undefined   //used to clear existing error message 
    		}
    	case 'getCounterDone':
    		return {
				...state, 
				isFetching: false, //used to indicate that the ajax request is running
				error: undefined,  //used to clear existing error message
				value: action.counter.theValue
			}    		
    	case 'getCounterFailed':
    		return {
				...state, 
				isFetching: false,  //used to indicate that the ajax request is running
				error: action.error //used to show the error message
			}    		    		
		...
    }
}


//the ajax calling as an "action"
function getCounterAction(){
	const actionAsFunction =  function(dispatch){
		
		dispatch({type:'getCounterStart'}); //dispatch an "loading started" action first
		
		return axios.get("http://localhost:12180/fo/rest/counter/get")
				.then(function(response){
					console.log(response.data); //success
					dispatch({type:'getCounterDone', counter: response.data});
				})
				.catch(function(error){
					if(error.response){ //failure with http response, such as a http 500 response
						dispatch({type:'getCounterFailed', error: error.response.error_description_for_user})
					}else{ //failure without http response
						dispatch({type:'getCounterFailed', error: "Unknown Error"})
					}
				});				
	}
	
	return actionAsFunction;  //so the action itself is a function
}




 




//the component   
class CounterComponent extends Component {
    constructor(props){
        super(props);
    }

    render(){
    	const errorStyle = {color:'red'};
    	
        return (

            <div>
                <h2>Counter</h2>                
                
                {/* Loading indicator */}
                {this.props.counter.isFetching &&  
                	<div>Loading...</div>
                }
                
                {/* Show error */}                 
                {this.props.counter.error && 
                	<div style={errorStyle}>{this.props.counter.error}</div>
                }
                
                {/* Show values */}                
                <h3>{this.props.counter.value}</h3>                    
                <div>
		            <button onClick={ e => this.props.onChange(1)}>+</button>   
		            <button onClick={ e => this.props.onChange(-1)}>-</button>    
                </div>
                                               
            </div>
        );
    }

}



class CounterComponentContainer extends Component {
	componentDidMount(){
		this.props.dispatch(getCounterAction()); //fetch the value when component is first rendered
	}
	
	render(){
		return (
				<CounterComponent counter={this.props.counter} onChange={this.props.onChange}/>
		);
	}
}

CounterComponentContainer = connect(
    mapStateToProps,
    mapStateToDispatch
) (CounterComponentContainer);  



export default CounterComponentContainer;

What will the window.origin be in html5 sandbox iframes?

I did a test about this.


If

  • Your site is www.host.com
  • The iframe src is also www.host.com

Then

  • sanbox + "allow-same-orign" enabled => window.origin will be "www.host.com"
  • sanbox + "allow-same-orign" DISABLED => window.origin will be null

If

  • Your site is www.host.com
  • The iframe src is www.guest.com

Then

  • sanbox + "allow-same-orign" => window.origin will be "www.guest.com"
  • sanbox + "allow-same-orign" DISABLED => window.origin will be null

Redux weird thing 3 – Action Creator

You can dispatch an action inside your component, for example, when a button is clicked, like

 
    render(){
        return (

            <div>
               ...
                <div>
                    <button onClick={ e =>  this.context.store.dispatch({type: 'counterChange', amount:  1}) }> + </button>
                    <button onClick={ e =>  this.context.store.dispatch({type: 'counterChange', amount: -1}) }> - </button>
                </div>
            </div>
        );
    }

 

But a more preferred way is to extract the action creation logic as another function.

function counterChangeAction(amount){
    return {
        type: 'counterChange',
        amount: amount
    }
}


class CounterComponent extends Component {
    constructor(props){
        super(props);
    }

    render(){
        return (

            <div>
                <h2>Counter</h2>
                <h3>{this.context.store.getState().counter.value}</h3>
                <div>
                    <button onClick={ e =>  this.context.store.dispatch(counterChangeAction(1)) }> + </button>
                    <button onClick={ e =>  this.context.store.dispatch(counterChangeAction(-1)) }> - </button>
                </div>
            </div>
        );
    }

}

You will get documenting benefits if you put all the actions together, just like you get the benefits when you put all the sql together in IBatis

Redux – avoid to pass props down component tree manually – 4. Build container components more easily with connect()

With connect() you don’t have to manually write the container component as a Component class.

 
const CounterComponentContainer = connect(
		// store.state => props of child component. Normally it's extracted as a function called mapStateToProps()
		state => { 
			return {counter: state.counter} 
		}, 
		// store.dispatch => props of child component. Normally it's extracted as a function called mapStateToDispatch()
		dispatch => {
			return {onChange: (amount) => dispatch({type: 'counterChange', amount: amount})} 
		}
) (CounterComponent);    //connect() returns a function.  Here you invoke the returned function with the original component as the parameter.

//And the following is not needed any more. You can comment it out. 
// CounterComponentContainer.contextTypes = {
//		store: PropTypes.object
// };
 

And you can go even futher. You don’t have to give the conainer component a name.

CounterComponent = connect(
		
		state => { 
			return {counter: state.counter} 
		}, 
		
		dispatch => {
			return {onChange: (amount) => dispatch({type: 'counterChange', amount: amount})} 
		}
) (CounterComponent);  //CounterComponent will be changed from a reusable component to a container component


class AppComponent extends Component {
  render() {
    return (

      <div className="App">
        ...
        <CounterComponent/>   <!-- This is still a container component -->
        ...
      </div>

    );
  }
}

This is it! Nothing sucks any more!

Redux – avoid to pass props down component tree manually – 3. Use an extra container component to keep the original one reusable

So a "container" component will be introduced to wrap the original one. The container component will adapt the store structure to "input parameters" the original one needs. Therefore, the original one doesn’t need to access store directly and will be reusable again.

//The reusable component, which doesn't access store any more 
class CounterComponent extends Component {
    constructor(props){
        super(props);
    }

    render(){
        return (

            <div>
                <h2>Counter</h2>
                <h3>{this.props.counter.value}</h3>    
                <div>
                    <button onClick={ e => this.props.onChange(1)}>+</button>   
                    <button onClick={ e => this.props.onChange(-1)}>-</button>    
                </div>
            </div>
        );
    }

}

// the container component, which gets data from the store and passes them as props to the reusable one 
class CounterComponentContainer extends Component {

    render(){
    	 return (        		
    			 <CounterComponent 
    			 	counter={this.context.store.getState().counter}
    			 	onChange={(amount) => this.context.store.dispatch({type: 'counterChange', amount: amount})}
    			 />	
    	        );
    }
}

CounterComponentContainer.contextTypes = {
		store: PropTypes.object
};




class AppComponent extends Component {
  render() {
    return (

      <div className="App">
        ...
        <CounterComponentContainer/>   <!-- Now it uses the container component -->
        ...
      </div>

    );
  }
}

Redux – avoid to pass props down component tree manually – 2. Use Provider to make store accessible to all components

This way every component in the tree can access the store directly. So you don’t need to pass the store manually any more

This solution is actually based on React’s Contenxt.

function render(){
		
    ReactDOM.render(
    	<Provider store = {rootStore}>	
    		<AppComponent appName="React Demo Site"/>
    	</Provider>,
    	
        document.getElementById('root'));
}


class AppComponent extends Component {
  render() {
    return (

      <div className="App">
	  	...
        <CounterComponent/>
        ...
      </div>
    );
  }
}


class CounterComponent extends Component {
    constructor(props){
        super(props);
    }

    render(){
        return (

            <div>
                <h2>Counter</h2>
                <h3>{this.context.store.getState().counter.value}</h3>
                <div>
                    <button onClick={ e =>  this.context.store.dispatch({type: 'counterChange', amount:  1}) }> + </button>
                    <button onClick={ e =>  this.context.store.dispatch({type: 'counterChange', amount: -1}) }> - </button>
                </div>
            </div>
        );
    }

}

//You need this only if a component needs to retrieve data from the context
CounterComponent.contextTypes = { 
	store: PropTypes.object
}; 


But it still sucks in that the CounterComponent is coupled with the root store structure.

  • Not good in terms of ecapsulation.
  • The CounterComponent is not really reuable, if another app with another store structure wants to contain it

Redux – avoid to pass props down component tree manually – 1. Pass the store down

In the basic example you can see props are passed down the compoent tree manually at every level. It’s very anoying.

A simple way to avoid this is to pass Redux store down at every level. It will simplify the code to some extent

function render(){
    ReactDOM.render(
        <AppComponent appName="React Demo Site"  store={rootStore}/>,
        document.getElementById('root'));
}




class AppComponent extends Component {
  render() {
    return (

      <div className="App">
        ...

        <CounterComponent store = {this.props.store}/>
        ...
      </div>

    );
  }
}



class CounterComponent extends Component {

    render(){
        return (

            <div>
                <h2>Counter</h2>
                <h3>{this.props.store.getState().counter.value}</h3>
                <div>
                    <button onClick={ e =>  this.props.store.dispatch({type: 'counterChange', amount:  1}) }> + </button>
                    <button onClick={ e =>  this.props.store.dispatch({type: 'counterChange', amount: -1}) }> - </button>
                </div>
            </div>
        );
    }

}

The problem is, you still have to pass down the store object everywhere. Still anoying.

Redux weird thing 1 – combineReducers

If your root component has more than one child component, you will have a big state , so is your reducer

// the initial state
const initialState = {
    counter: {
        value: 0
    },
    
    toDo: {
    	itemList: [
 			      {id: 1, text: "be born", completed: true},
 			      {id: 2, text: "get married", completed: true},
 			      {id: 3, text: "have a baby", completed: false}
 			]	 		
    }    
};

 
function reducer(state=initialState, action){
    switch(action.type){
        case 'counterChange':
            let amount = action.amount ? action.amount: 0;
            return {
                ...state,
                counter: {
                    value: state.counter.value + amount
                }
            };

        case 'toDoItemStatusChange':
            return {
        		...state,
        		toDo:{
            		itemList: state.toDo.itemList.map(item => {
            			if(item.id == action.id){
            				return {
            					...item,
            					completed: action.completed
            				}        				  
            			}else{
            				return item;
            			}
            			
            		})        			
        		}

        	};
        
        case 'toDoNewItem': 
        	return {
        		...state, 
        		toDo:{
            		itemList: state.toDo.itemList.concat({
            			id: Math.max.apply(null, state.toDo.itemList.map(item => item.id)) + 1,
            			text: action.text,
            			completed: false
            				
            		})        			
        		}
        	
        	};

        default:
            return state;
    }

}

 
const rootStore = createStore(reducer);

Not only the things are big, but also the paths are deep.

The solution is to divide the big things in the code, but combine them during runtime. See below. 


 function counterReducer(state={value:0}, action){
    switch(action.type){
	    case 'counterChange':
	        let amount = action.amount ? action.amount: 0;
	        return {
	            ...state,             
	            value: state.value + amount             
	        };
	    default:
	        return state;
    }
}
 

const initialToDoState = {
		 
    	itemList: [
 			      {id: 1, text: "be born", completed: true},
 			      {id: 2, text: "get married", completed: true},
 			      {id: 3, text: "have a baby", completed: false}
 			]	 		       
};

function toDoReducer(state=initialToDoState, action){
    switch(action.type){
	
	    case 'toDoItemStatusChange':
	        return {
	    		...state,
	    		
	        	itemList: state.itemList.map(item => {
	        			if(item.id == action.id){
	        				return {
	        					...item,
	        					completed: action.completed
	        				}        				  
	        			}else{
	        				return item;
	        			}
	        			
	        	})        				    		
	    	};
	    
	    case 'toDoNewItem': 
	    	return {
	    		...state, 
	    		 
	        	itemList: state.itemList.concat({
	        			id: Math.max.apply(null, state.itemList.map(item => item.id)) + 1,
	        			text: action.text,
	        			completed: false
	        				
	        	})        			
	    		 
	    	
	    	};
	
	    default:
	        return state;
    }	    
}



const reducer = combineReducers({
	counter: counterReducer,
	toDo: toDoReducer
});

 
const rootStore = createStore(reducer);