kemperrr

Пример Реализация чата на React JS

Recommended Posts

kemperrr    125

Чат написан с использованием ReactJS, собирает это все webpack, ну и для удобства я использую babel ( ES6 )

Выкладываю только исходники!

ВНИМАНИЕ ГАВНОКОД ^_^

 
import React from 'react'
import ReactDOM from 'react-dom'

class BaseChatComponent extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            input: '',
            showInput: 0,
            messages: [],
            historyPosition: 0,
            historyMessages: []
        }
    }

    componentWillMount() {
		window.insertMessageToChat = this._insertMessageToChat.bind(this)
        this._historyInputChatMessage = this._historyInputChatMessage.bind(this)
        document.addEventListener("keyup", this._handleKeyDown.bind(this));
	}

    _handleKeyDown(e) {
        
        if(e.keyCode == 84 && this.state.input == '') {
            this.setState({showInput: 1})
            mp.invoke("focus", true)
        }
        if(e.keyCode == 27 && this.state.showInput) {
            this.setState({showInput: 0, input: ''})
            mp.invoke("focus", false)
        }
        if(e.keyCode == 13 && this.state.input != '') {
            if(this.state.input.length > 0)
            {
                this._historyInputChatMessage(this.state.input)
                if(this.state.input[0] == "/")
                {
                    let value = this.state.input.substr(1);
                    if(value.length > 0)
                        mp.invoke("command", value);
                }
                else
                {
                    mp.invoke("chatMessage", this.state.input);
                }
            }
            mp.invoke("focus", false)
            this.setState({showInput: 0, input: ''})
        }
        else if(e.keyCode == 13 && this.state.showInput && this.state.input == '') {
            this.setState({showInput: 0, input: ''})
            mp.invoke("focus", false)
        }
        if(e.keyCode == 40 && this.state.showInput) {
            if (this.state.historyPosition > 0) {
                this.state.historyPosition -= 1;
                if (this.state.historyPosition == 0 && this.state.input != '') {
                    this.setState({input: ''})
                    return;
                }
                this.setState({input: this.state.historyMessages[this.state.historyMessages.length - this.state.historyPosition]})
            }
            
        }
        if(e.keyCode == 38 && this.state.showInput) {
            if (this.state.historyPosition < this.state.historyMessages.length) {
                this.state.historyPosition += 1;
                this.setState({input: this.state.historyMessages[this.state.historyMessages.length - this.state.historyPosition]})
            }
            
        }
    }

    _insertMessageToChat(str) {
        this.setState((prevState) => {
            return prevState.messages.push(str)
        });
        this.scrollToBottom();
        if (this.state.messages.length > 50)
            this.state.messages.splice(0, 1);
    }

    _historyInputChatMessage(str)
    {
        this.state.historyPosition = 0
        if (this.state.historyMessages.length === 0 || (this.state.historyMessages.length > 0 && this.state.historyMessages[this.state.historyMessages.length - 1] !== str))
            this.state.historyMessages.push(str);

        if (this.state.historyMessages.length > 50)
            this.state.historyMessages.splice(0, 1);

    }

    get isActiveInput(){
        return this.state.showInput ? 'block' : 'none'
    }

    handleInputChange(e) {
        this.setState({input: e.target.value})
    }

    scrollToBottom() {
        const scrollHeight = this.messageList.scrollHeight;
        const height = this.messageList.clientHeight;
        const maxScrollTop = scrollHeight - height;
        this.messageList.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0;
    }

    render() { 
        return (
            <div id = "chat" className = "ui_element" style = {{position: 'absolute'}}> 
                <ul id = "chat_messages" ref={(div) => {this.messageList = div}}>
                    {
                        this.state.messages.map((item, key) => {
                            return (
                                <li key = {key} dangerouslySetInnerHTML={{__html: item}}></li> 
                            )
                        })
                    }
                    
                </ul>
                <div><input id = "chat_msg" type = "text" ref = {input => input && input.focus()} value = {this.state.input} onChange = {this.handleInputChange.bind(this)} style = {{display: this.isActiveInput}} /></div>
            </div>
        )
    }
}

ReactDOM.render(<BaseChatComponent />, document.getElementById('content'));

 

Edited by kemperrr
  • Like 2

Share this post


Link to post
Share on other sites
nicita13    0
On 3/16/2017 at 6:34 PM, kemperrr said:

Чат написан с использованием RactJS, собирает это все webpack, ну и для удобства я использую babel ( ES6 )

Выкладываю только исходники!

ВНИМАНИЕ ГАВНОКОД ^_^

  Reveal hidden contents

import React from 'react'
import ReactDOM from 'react-dom'

class BaseChatComponent extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            input: '',
            showInput: 0,
            messages: [],
            historyPosition: 0,
            historyMessages: []
        }
    }

    componentWillMount() {
		window.insertMessageToChat = this._insertMessageToChat.bind(this)
        this._historyInputChatMessage = this._historyInputChatMessage.bind(this)
        document.addEventListener("keyup", this._handleKeyDown.bind(this));
	}

    _handleKeyDown(e) {
        
        if(e.keyCode == 84 && this.state.input == '') {
            this.setState({showInput: 1})
            mp.invoke("focus", true)
        }
        if(e.keyCode == 27 && this.state.showInput) {
            this.setState({showInput: 0, input: ''})
            mp.invoke("focus", false)
        }
        if(e.keyCode == 13 && this.state.input != '') {
            if(this.state.input.length > 0)
            {
                this._historyInputChatMessage(this.state.input)
                if(this.state.input[0] == "/")
                {
                    let value = this.state.input.substr(1);
                    if(value.length > 0)
                        mp.invoke("command", value);
                }
                else
                {
                    mp.invoke("chatMessage", this.state.input);
                }
            }
            mp.invoke("focus", false)
            this.setState({showInput: 0, input: ''})
        }
        else if(e.keyCode == 13 && this.state.showInput && this.state.input == '') {
            this.setState({showInput: 0, input: ''})
            mp.invoke("focus", false)
        }
        if(e.keyCode == 40 && this.state.showInput) {
            if (this.state.historyPosition > 0) {
                this.state.historyPosition -= 1;
                if (this.state.historyPosition == 0 && this.state.input != '') {
                    this.setState({input: ''})
                    return;
                }
                this.setState({input: this.state.historyMessages[this.state.historyMessages.length - this.state.historyPosition]})
            }
            
        }
        if(e.keyCode == 38 && this.state.showInput) {
            if (this.state.historyPosition < this.state.historyMessages.length) {
                this.state.historyPosition += 1;
                this.setState({input: this.state.historyMessages[this.state.historyMessages.length - this.state.historyPosition]})
            }
            
        }
    }

    _insertMessageToChat(str) {
        this.setState((prevState) => {
            return prevState.messages.push(str)
        });
        this.scrollToBottom();
        if (this.state.messages.length > 50)
            this.state.messages.splice(0, 1);
    }

    _historyInputChatMessage(str)
    {
        this.state.historyPosition = 0
        if (this.state.historyMessages.length === 0 || (this.state.historyMessages.length > 0 && this.state.historyMessages[this.state.historyMessages.length - 1] !== str))
            this.state.historyMessages.push(str);

        if (this.state.historyMessages.length > 50)
            this.state.historyMessages.splice(0, 1);

    }

    get isActiveInput(){
        return this.state.showInput ? 'block' : 'none'
    }

    handleInputChange(e) {
        this.setState({input: e.target.value})
    }

    scrollToBottom() {
        const scrollHeight = this.messageList.scrollHeight;
        const height = this.messageList.clientHeight;
        const maxScrollTop = scrollHeight - height;
        this.messageList.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0;
    }

    render() { 
        return (
            <div id = "chat" className = "ui_element" style = {{position: 'absolute'}}> 
                <ul id = "chat_messages" ref={(div) => {this.messageList = div}}>
                    {
                        this.state.messages.map((item, key) => {
                            return (
                                <li key = {key} dangerouslySetInnerHTML={{__html: item}}></li> 
                            )
                        })
                    }
                    
                </ul>
                <div><input id = "chat_msg" type = "text" ref = {input => input && input.focus()} value = {this.state.input} onChange = {this.handleInputChange.bind(this)} style = {{display: this.isActiveInput}} /></div>
            </div>
        )
    }
}

ReactDOM.render(<BaseChatComponent />, document.getElementById('content'));

 

А не подскажешь как принимать входящие сообщения?  Я вижу что для отправки команд или просто сообщения можно вызвать метод mp.invoke, но не понятно как принимать входящие сообщения. Заранее спасибо.

Share this post


Link to post
Share on other sites
Sportik    0

Ошибка "ReferenceError: document is not defined" вылетает при запуске сервера. Как исправить?

Share this post


Link to post
Share on other sites
nicita13    0

@Sportik этот код запускается не на сервере, а должен передаться клиенту и там он уже будет исполняться.

@Sportik document  - это глобальная браузерная переменная, которой ни в ноде, ни на серваке нет. в данном случае в качестве браузера выступает cef.

Share this post


Link to post
Share on other sites
Danil_Valov    19
window.insertMessageToChat = this._insertMessageToChat.bind(this)
this._historyInputChatMessage = this._historyInputChatMessage.bind(this)

Это в конструкторе должно быть после this.state = {}

document.addEventListener("keyup", this._handleKeyDown.bind(this));

Это должно быть в componentDidMount, и при этом должен быть document.removeEventListener() в componentWillUnmount, иначе при повторном заходе в этот компонент у тебя повторно навесится бинд и будет отрабатывать 2 раза: старый, не снятый, и новый. И лучше добавить

this._handleKeyDown = this._handleKeyDown.bind(this);

в constructor к остальным биндингам, а тут использовать

componentDidMount() {
  document.addEventListener("keyup", this._handleKeyDown);
}
componentDidUnmount() {
  document.removeEventListener("keyup", this._handleKeyDown);
}

 

Советую все функции класса, в которых нужен доступ к this (this.state, this.props etc.) биндить в конструкторе на this. И .bind(this) вообще кроме конструктора нигде не использовать. Это считается хорошим тоном.

И ещё, prevState лучше получать через this.state. Это немного быстрее (обращение к существующему объекту к классе), чем использование this.setState с функцией на входе.

 

И вот так вот делать категорически нельзя:

_historyInputChatMessage(str)
    {
        this.state.historyPosition = 0
        if (this.state.historyMessages.length === 0 || (this.state.historyMessages.length > 0 && this.state.historyMessages[this.state.historyMessages.length - 1] !== str))
            this.state.historyMessages.push(str);

        if (this.state.historyMessages.length > 50)
            this.state.historyMessages.splice(0, 1);

    }

Если менять state через this.state.value = '', то при первом же изменении компонента (this.setState() или изменение props) у тебя все твои изменения в this.state откатятся. Используй this.setState() здесь вот так:

    _historyInputChatMessage(str) {
        const {historyMessages} = this.state;
        const newHistoryMessages = historyMessages.slice(0);
      
        if (
          !newHistoryMessages.length ||
          (
            newHistoryMessages.length > 0 &&
            newHistoryMessages[newHistoryMessages.length - 1] !== str
          )
        ) {
            newHistoryMessages.push(str);
        }
      
        this.setState({
          historyPosition: 0,
          historyMessages: newHistoryMessages.slice(0, 50)
        });
    }

 

А вообще, молодец! Очень круто! Развивайся дальше! Будут вопросы - пиши.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.