Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

velocity-react - animating scrollTop after component update

I'm writing a simple "console" that shows messages in a chat-like manner. With messages appearing from the bottom, and moving up.

I have the working code, but I'd like to animate the appearing messages by scrolling the container to the bottom each time a new "li" is added.

Current code:

import React from 'react';
import { render, findDOMNode } from 'react-dom';


export default React.createClass({
    componentDidUpdate : function(){
        var node = findDOMNode(this);
        node.scrollTop = node.scrollHeight;
    },
    render() {
        return (
            <ul id="log">
            {
                this.props.messages.map(function(message, index){
                    return <li key={index}>[{message.time.format('HH:mm:ss')}] {message.action}</li>
                })
            }
            </ul>
        )   
    }
})

The messages prop comes from the parent component and the store.

I found this velocity plugin: https://github.com/twitter-fabric/velocity-react and I can't figure out how to use it in my situation. All the examples don't seem to apply (or maybe I just don't understand them).

I'm quite new to react, and some concepts still confuse me, so please be understanding.

I don't want to use jQuery.

like image 603
Piotr Ma'niak Avatar asked Feb 23 '16 00:02

Piotr Ma'niak


1 Answers

The velocity-react plugin provides already implemented React components for using Velocity's animations.

I guess that the scroll functionality can be implemented via animations too, but Velocity library has scroll command. I've reveiwed velocity-react plugin and it provides interface (components) for the animations. There isn't any support for the Velocity commands.

It's pretty easy to use Velocity commands in React. I've created react-velocity-scroll repo based on your question and there is a live demo of sending/listing messages in a chat-like manner.

Please note that the Velocity library is included in the example via velocity-react plugin. Its recommended to include the plugin for future advanced animations, because it provides already implemented React components for using Velocity's animations. However the repo don't use any animations. It uses only Velocity library's scroll command. If you prefer - you can import Velocity library independently.

However, here are my components. Please focus on MessageItem component - once a new message is being added, then scroll down to it.

App

import React from 'react';
import MessagesList from './MessagesList';

const style = {
    textAlign: 'center'
};

class App extends React.Component{
    constructor(props) {
        super(props);

        this.state = {
            /**
             * @type Array - Store sent messages
             */
            messages: [],
            /**
             * @type String - Store the input value.
             * It's reset on message sent
             */
            text: ''
        }
    }

    handleOnChange(e) {
        const text = e.target.value;
        this.setState({ text });
    }

    handleOnKeyPress(e) {
        const text = e.target.value;

        // Send the message on `Enter` button press
        if (e.key === 'Enter') {
            this.sendMessage(text);
        }
    }

    /**
     * Add the message to the state and reset the value
     * of the input
     *
     * @param String text - Message text
     */
    sendMessage(text) {
        const { messages } =  this.state;
        const message = { date: new Date(), text };

        this.setState({
            messages: messages.concat([message]),
            text: ''
        });
    }

    render() {
        const { messages, text } = this.state;

        return <div style={style}>
            <h1>Please enter your text message:</h1>

            <input
                value={text}
                placeholder="Press Enter for sending"
                onChange={this.handleOnChange.bind(this)}
                onKeyPress={this.handleOnKeyPress.bind(this)} />

            <MessagesList messages={messages} />
        </div>
    }
}

export default App;

MessagesList

import React from 'react';
import MessageItem from './MessageItem';

const style = {
    height: '100px',
    overflowY: 'scroll'
};

const MessagesList = (props) => {
    let { messages } = props;

    messages = messages.map( function(message, index){
        return <MessageItem key={index} index={index} message={message} />
    });

    return <ul style={style}>{messages}</ul>
};

export default MessagesList;

MessageItem

import React from 'react';
import ReactDOM from 'react-dom';
const Velocity = require('../node_modules/velocity-react/lib/velocity-animate-shim');

const style = {
    listStyle: 'none'
};

class MessageItem extends React.Component{
    componentDidMount() {
        const parentNode = ReactDOM.findDOMNode(this).parentNode;
        const node = ReactDOM.findDOMNode(this);

        // Once a new item is being added, then scroll down to it
        Velocity(node, 'scroll', {
            duration: 500,
            container: parentNode,
            queue: false
        });
    }

    render() {
        const { message } = this.props;

        return <li style={style}>{message.date + ' - ' + message.text}</li>
    }
}

export default MessageItem;
like image 189
Jordan Enev Avatar answered Nov 15 '22 14:11

Jordan Enev