Gradle upload a custom-generated jar file to Nexus

Do this:

apply plugin: 'maven-publish'


   publishing {
        publications {
            maven( MavenPublication ) {
                artifact theTaskThatGeneratesJar
            }
        }
        repositories {
            maven {
                def releasesRepoUrl = http://.../repositories/releases/'
                def snapshotsRepoUrl = 'http://.../repositories/snapshots/'
                url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
                credentials {
                    username 'xxx'
                    password 'xxx'
                }
            }
        }

    }


Sending html emails with images inside

If the images are external image links that can be accessed by http url, then nothing speical needs to be done.

If the images are external image files, then you have 2 choices.

Embed the images as base64 strings. . This is simple. However email clients such as gmail will refuse to render them

Using cid. This is the most robust way. An example can be found here . In addition, you should set the content type of the image body part as "image/png", otherwise the email receipients will see there are attachments

     
              MimeBodyPart imagePart = new MimeBodyPart();
                DataSource fds = new FileDataSource(imageFile);
                imagePart.setDataHandler(new DataHandler(fds));
                imagePart.setHeader("Content-ID", "<" + imageFile.getName() + ">");
                imagePart.setHeader("Content-Type", "image/png");

React Component: A pair of componentDidMount() and componentWillUnmount() may cause infinite loop if error is thrown from render

The following will cause an infinite loop. The render() method will be called again and again.

class ToDoItemEditComponentContainer extends React.Component {
    componentDidMount() {        
        this.props.loadItem();  //load item remotely and put it in the local redux store      
    }

    componentWillUnmount() {
        this.props.clearItemLocally(); //remove item from local redux store
    }

    render() {
        throw "some error" 
    }
}

Here is why: "As of React 16, errors that were not caught by any error boundary will result in unmounting of the whole React component tree. " (See here). To be more specific,

  • render() throws an error
  • The react component tree is unmounted
  • componentWillUnmount() is called. And something is cleared from the reduxt store or react state
  • The component tree will be rendered again since some state has been changed

To fix: Implement componentDidCatch() hook in this component or create a global error boundary as shown in here . When there is a render() error, do not call the render() method any more, or do not change any local data in componentWillUnmount()

My practice of fetching data for List/Detail components in React/Redux

The user experience it should have

  • When you go to a detail page from the list page or with a direct URL, the detail page should show the up-to-date data
  • When you go to the list page with a direct URL, the list page should show the up-to-date data
  • When you RETURN to the list page from a detail page, the list page doesn’t have to show the up-to-date data.
  • When you RETURN to the list page from a editing page after MAKING CHANGES, the list page should show the up-to-date data to reflect the changes

Solution

  • On the list page
    • When entering the component, only fetch the data if the current data (this.props.listData) is null
    • When leaving the component, don’t clear the data fetched from Redux store. They will be needed when returned from detail pages
  • On a detail page
    • When entering the component, always fetch the data from the backend
    • When leaving the component, always clear the data locally from the redux store, otherwise the data of this item may be shown when you visit the detail page of another item. That’s because data fetching is called after render() .
    • When returning to the list page from here without any change, do nothing about the list in the redux store. And the link of returning should be an SPA link, which doesn’t force a new document creation
    • After data changing, dispatch an action to refresh the list data in the redux store. So when you go back to the list page, the data has been changed to reflect the changes

Code implementation

//List
class ListComponentContainer extends React.Component {
    componentDidMount() {
        if (this.props.itemList == null) {  
            this.props.dispatch(getItemsAsyncAction(this.props.token));
        }
    }

    render() {
        if (this.props.itemList) {
            return <ListComponent {...this.props}>;
        } else {
            return null;
        }
    }
}

//Detail
class ItemEditComponentContainer extends React.Component {
    componentDidMount() {
       dispatch(getCurrentItemAsyncAction(itemId));
    }

    componentWillUnmount() {
        dispatch(clearCurrentItemLocallyAction());
    }

    render() {
            if (this.props.currentItem) {
                return <ItemEditComponent  currentitem={this.props.currentItem}/>;
            } else {
                return null;
            }
    }
}

//and in the method of handling changes
    onSubmit() {
        dispatch(updateItemAsyncAction());
        dispatch(clearItemListLocallyAction());  //1.  getItemsAsyncAction() will also work.  2. This can also be put in the success callback of updateItemAsyncAction()
    }

What happens to the redux store if I refresh the browser?

It will go away, and then be initialised.

If you have this question, it means you have no exact idea of the lifespan of client-side state in a browser.

First of all, a document is created under current tab of the browser when a url is first requested from the server side. The client-side state’s lifespan is dependent on the lifespan of the document.

If your website is not an SPA, then clicking a link will lead to another http request for the server, and a new document instance is created. The client-side state will all disappear.

If your document is an SPA based on some router framework, e.g. react-router, and when you click a router-based link, no new http request will be sent, and the document remains the same one. How is this done? The onClick handlers of these links will first do e.preventDefault(), then call the History API such as history.pushState() to do same-document navigation.

How is this going to affect redux-store in an SPA site?

When you refresh the current page, a new document is created, the existing redux store will go away. After the new document is loaded, the redux store will be initialized according to your redux bootstrapping logic.

When you request a deep-link url directly in the browser, the same as above will happen.

When you click a router-based link, it will be a same-document navigation. The redux store will still be there.

When you click back/forward of the browser, then same as above will happen.

When you click a link that’s not router-based, i.e., a primitive link, the browser will just request a new document from the server side, and the existing redux store will go away

For more details about navigation in an SPA and a non-SPA, read this great article: How Single-Page Applications Work

Load data from backend before rendering a React component – best practice

Where to put the data fetching code?

componentWillMount() ? Not any more. It’s been deprecated.

You have no choice but put it inside componentDidMount() , which is recomendded by React.

However ther is a big issue. componentDidMount() is actually AFTER render(). It "works" because after componentDidMount() finishes its job and changes some state, render() will be called again. So render() will be called twice. The first time of rendering may break

The solution

  • Put the logic inside componentDidMount()
  • Inside render() method, check if the data is ready. If yes, render the component; otherwise, render null
  • When data is loading, show a spin or a loading bar. This way the user won’t be confused by empty content
  • Clean the state inside componentWillUnmount(). If you don’t do this, the data is alwasy "ready" at the step above, so no empty content will be shown
  • Better use a connector component to do all this dirty work

Code Sample

//1,2,3,4,5,6 in the comment is the order that the statements are executed 

class UserProfileComponentContainer extends React.Component { // the container component 
    componentDidMount() {
        this.props.dispatch(getMyProfileAsyncAction(this.props.rootState.token));   //2
    }

    componentWillUnmount(){
        this.props.dispatch(cleanProfileLocallyAction());  //5  (when the user leaves the component) 
    }

    render() {
        if (hasUserProfile(this.props.rootState)) { //data is ready 
            return <UserProfileComponent userProfile={this.props.rootState.userProfile}/>   //4 
        } else {
            return null;  //1, 3, 6 (when the user come back to this component) 
        }
    }
}