Author Archives: Chen Jian

Not every kind of state needs to go to Redux

Only some kinds of state need to be in Redux,

  • State that’s shared by multiple components on the same page
    • State that’s read by multiple components
    • State that’s written by one component and read by another
  • State whose lifecycle is longer than its component. For example, “recordList” for a list page – If you go to a detail page from a list page, and then come back, you don’t want to re-fetch the list from the backend. With “recordList” in redux you can just read it from the Redux store.

Typical cases that you don’t need Redux for you state:

  • Form values – And this is why Formix is better than Redux-Form
  • “currentRecord” for a detail page

Best practice of html5 input for limited length

What you need is ,

  • Only numbers can be input
  • The length of input is limited to N, say 2
  • On mobile devices, a number pad will be shown

And the way to do it is:

<input type="number" pattern="[0-9]*" oninput="this.value=this.value.slice(0,2)"/>

If you are using material-ui, it will be

<TextField
    inputProps={{
        type: "number",
        pattern: "[0-9]*" 
    }}

    onInput={(e: any)=>{
        e.target.value = e.target.value.slice(0,2)
    }}
/>

Generate assertEquals(xxx, bean.getXxx()) for all the properties of a javabean

In intellij, create such a live template:

    public static void main(String args[]) {
        Class<?> clazz = $Bean$.class;
        String object = "$bean$";
        java.lang.reflect.Method[] methods = clazz.getMethods();
        for (java.lang.reflect.Method m : methods) {
            if ((m.getName().startsWith("get") || m.getName().startsWith("is") )&&  (!m.getName().equals("getClass")) &&  m.getParameterTypes().length == 0) {
                System.out.println(
                    "assertEquals(\"xxx\"," + object + "." + m.getName() + "()); "
                );
            }
        }

    }

Make sure chromedriver can run with Java selenium code, both in a real machine and in docker

Install chromedriver

You want to make the following line of code run successfully.

ChromeDriver driver = new ChromeDriver();

In windows, you need to download chrome driver from https://chromedriver.chromium.org/downloads and put it in system’s path (An alternative is to set the executable file’s path as a system property “webdriver.chrome.driver”, not beautiful)

In *nix or linux-based Docker, you can

apt update
apt install chromedriver -y   #for debian-based systems 

Options needed for ChromeDriver

        ChromeDriverService chromeDriverService = new ChromeDriverService.Builder().withWhitelistedIps("").build();//without this you may see " bind() returned an error, errno=99: Cannot assign requested address (99) "

...
        // without the following you may see "DevToolsActivePort file doesn't exist" 
        ChromeOptions chromeOptions = new ChromeOptions();
        if(isUnixBased()) {
            chromeOptions.addArguments("--headless");
            chromeOptions.addArguments("--no-sandbox");
            chromeOptions.addArguments("--disable-dev-shm-usage");
        }

...
        ChromeDriver driver = new ChromeDriver(chromeDriverService, chromeOptions);

					

Opinion: Don’t use class for data structure in typescript (2019). Use interface only

Always use interfaces instead

Problems of class:

  • Lots of frameworks such as Redux don’t support class instances. They only support plain objects. There are tools that can convert plain objects to class instances, but the point below makes it awkward.
  • Given a variable “someType: SomeType” in the code, you don’t know if it is a plain object or a class instance. So you don’t know if you can call its methods, or if it is OK to call plainToClass() to convert it to a class instance.

Use spring +aspectj in Spring boot

compile('org.springframework.boot:spring-boot-starter-aop')
compile('org.aspectj:aspectjrt')
compile('org.aspectj:aspectjweaver')
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class YourApplication ...{
...
}
@Component
@Aspect
public class YourAspect {

	@Before("execution(public * com..*Manager.*(..))")
	public void doAdvice(JoinPoint joinPoint) {...}
	 
}

Enable google analytics with react-router

One way of doing this is to create your own Router class, which extends the one from the framework; and inside this Router class you put in the tracking logic:

class MyRouteComponent extends React.Component<Props, State> {

    constructor(props: Props) {
        super(props);
        this.state = {currentUserRefreshed: false};

        if (isProdEnv()) {
            ReactGA.initialize('UA-GOOGLE-ANAYLTICS-ID');
        }
    }

    trackPage(pageUrl:string) {
        ReactGA.set({page: pageUrl});
        ReactGA.pageview(pageUrl);
    }

    toPageUrl(location: Location) {
        return location.pathname + location.search;
    }

    componentDidMount() {
        this.props.refreshCurrentUser();

        if(isProdEnv() && this.props.location){
            this.trackPage(this.toPageUrl(this.props.location!));
        }
    }

    render() {
        ...
    }
}

My first react HOC + Redux component in Typescript

The logic: Show a component only if the user logged in

import React from 'react';
import {connect} from "react-redux";

interface AuthenticatedUserOnlyProps {
    authenticated: boolean
}


function mapStateToProps(state: State) {    
    return {
        authenticated: state.authenticated
    };
}

export function authenticatedUserOnly<T>(WrappedComponent: React.ComponentType<T>) {
    class FinalComponent extends React.Component<AuthenticatedUserOnlyProps > {
        public render() {

            const {authenticated, ...otherProps} = this.props;
            if (authenticated) {
                return <WrappedComponent {...(otherProps as T)}/>
            } else {
                return null;
            }
        }
    };

    return connect(mapStateToProps, null)(FinalComponent);
}

To use it, just

render() {
    const someComponent = authenticatedUserOnly(SomeComponent); 

    return {
        <div>
           {someComponent }
        </div>
    }
}

Springfox for 204 response

What you need to,

  • Return ResponseEntity<Void>
  • Set the @ResponseStatus for your rest controller’s method

If you don’t do that, you will get ResponseEntity as the response class in your swagger json, and you will get both 200 and 204 as your successful response code, which is not correct because your method only returns 204, no 200.

    @RequestMapping(value = "...", method = RequestMethod.POST)
    @ApiOperation(value = "...")
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "success")
    })
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public ResponseEntity<Void> someMethod(){}