kemperrr

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

13 posts in this topic

Posted (edited)

Чат написан с использованием 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
2 people like this

Share this post


Link to post
Share on other sites
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

"Чат написан с использованием RactJS". Исправь на ReactJS

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

@nicita13 Каким образом запустить на стороне клиента не подскажешь? 9_9

Share this post


Link to post
Share on other sites
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.