Ad

React Animation Based On A Timer

- 1 answer

Here is my problem: when a user clicks on a submit button, I want to slide in a banner from the top of the page if the submit wasn't successful (i.e. due to network problem).

After the banner has being displayed for 2 seconds, I want it to retract and go out of the view.

Here is a snippet of my code:

export default class Base extends React.Component {
    ...

    renderErrMsg = () => {
        if(this.props.errMsg){
            return (
                <div className="errMsg">
                    {this.props.errMsg}
                </div>
            )
        }
    }

    render(){
        return(
            <div>
                {this.renderErrMsg()}
                ...
            <div>
        )
    }
}

I tried to wrap #errMsg with ReactCSSTransitionGroup but it gives me an error:

warning.js:45 Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of Base.warning

Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of Base.


UPDATE: @Ted definitely pointed me to the right solution. In his solution, a state has been created to mirror the value stored in a props. I realised that there is no need to create the state variable by using componentWillReceiveProps instead of componentWillUpdate:

componentWillReceiveProps(nextProps) {
    if (nextProps.application.errorMessage){
        setTimeout(() => {
            this.props.dispatch(removeErrMsg())
        }, 3000)
    }
}

I did have to write the removeErrMsg action so that dispatch can clear the error message in the store once it is shown.

The mistakes that I made:

  1. my import statment was wrong: I had import { ReactCSSTransitionGroup } from 'react/addons', but it should be import ReactCSSTransitionGroup from 'react-addons-css-transition-group'

  2. When rendering the error message, the ReactCSSTransitionGroup portion should be inside the render() method instead of the renderErrMsg method. renderErrMsg returns the content to be animated but it shouldn't return the TransitionGroup along with it. Otherwise, there won't be any animation. Because when there is no error message, the entire errMsg div along with TransitionGroup becomes null and the error message div will disappear instead of retract.

Ad

Answer

First you should transfer your props to components state so it can be cleared after delay. Use componentWillReceiveProps lifecycle method to update state and clear it after delay you want. For transition, you need to set key attribute on child of <Transition> element (since you are animating only one child item key can be anything). Also, your renderErrMsg() method should always return a value so add else return null in your if statement.

Here is a working demo: http://codepen.io/teodragovic/pen/KVqgYg?editors=011

jsx:

const Transition = React.addons.CSSTransitionGroup;

const Example = React.createClass({

    getInitialState() {
        return {
            error: null
        };
    },

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>submit</button>
                <Inner error={this.state.error} />
            </div>
        );
    },

    handleClick() {
        this.setState({ error: 'error' });
    }

});

const Inner = React.createClass({

    getDefaultProps() {
        return {
            error: null
        };
    },

    getInitialState() {
        return {
            error: this.props.error
        };
    },

    componentWillReceiveProps(nextProps) {
        if (nextProps.error) {
            this.setState({ error: nextProps.error });
            const timer = setTimeout(() => {
                clearTimeout(timer);
                this.setState({ error: null });
            }, 3000);
        }
    },

    render() {
        return (
            <div>
                <Transition
                    component="div"
                    transitionName="item"
                >
                    {this.renderErrMsg()}
                </Transition>
            </div>
        );
    },

    renderErrMsg() {
        if (this.state.error) {
            return (
                <div className="errMsg" key={1}>
                    {this.state.error}
                </div>
            );
        } else {
            return null;
        }
    }


});

React.render(
    <Example />,
    document.getElementById('example')
);

css:

.errMsg {
    padding: 20px;
    border: 1px solid blue;
    text-align: center;
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    transform: translateY(-50%);
}

.item-enter {
    top: -50%;
    transition: all .5s ease-out;
}

.item-enter-active {
    top: 50%;
}

.item-leave {
    top: 50%;
    transition: all .5s ease-in;
}

.item-leave-active {
    top: -50%;
}
Ad
source: stackoverflow.com
Ad