Как я могу поддерживать состояние и «это» с помощью функций реагирующих компонентов - PullRequest
0 голосов
/ 19 марта 2019

В настоящее время, когда socket.io выдает «gmessage» с моего сервера, и сокет в моем компоненте перехватывает его, все мое состояние заменяется.

Таким образом, текущий поток выглядит следующим образом:

Я посылаю сообщение от компонента, оно отправляется на мой сервер, а затем в watson API.API отправляет ответ на мой сервер, а сервер отправляет его компоненту.

Таким образом, первое сообщение создает соединение с socket.io, затем второе событие socket.io перехватывается в том же соединении.

Есть ли лучший способ установить соединение с сокетом.io и обрабатываете как emit, так и "on gmessage" части этого?Спасибо за любые советы заранее.Я все еще новичок, чтобы реагировать, так что все, что вы видите, что я должен делать по-другому, полезно !!

...
import {Launcher} from 'react-chat-window'
import io from 'socket.io-client';

import { getUser } from '../actions/userActions';

class Workshop extends Component {

  constructor() {
      super();
      this.state = {
        messageList: []
      };
    }

  _onMessageWasSent(message) {
    this.setState({
      messageList: [message, ...this.state.messageList]
    })

    var socket = io.connect('http://localhost:5000');

    socket.emit('message', { message });

    var myThis = this
    var myState = this.state

    socket.on('gmesssage', function (data) {
      console.log(data);
      myThis.setState({
        messageList: [{
          author: 'them',
          type: 'text',
          data:  data
        }, ...myState.messageList]
      })
    })
  }

  render() {
    return (
      <Grid container className="DashboardPage" justify="center">
        <Grid item xs={12}>
          <div>Welcome to your Workshop</div>
          <TeamsTaskLists />
        </Grid>

        <Launcher
          agentProfile={{
            teamName: 'Geppetto',
            imageUrl: 'https://geppetto.ai/wp-content/uploads/2019/03/geppetto-chat-avi.png'
          }}
          onMessageWasSent={this._onMessageWasSent.bind(this)}
          messageList={this.state.messageList}
          showEmoji
        />

      </Grid>
    );
  }
}

const mapStatetoProps = state => ({
  user: state.user
});

export default connect(
  mapStatetoProps,
  { getUser }
)(Workshop);

Ответы [ 3 ]

3 голосов
/ 20 марта 2019

Fareed предоставил работающее решение Redux и предложил улучшения для подхода, не основанного на Redux, поэтому я хотел бы рассмотреть проблемы в вашем текущем коде:

  1. Вы переинициализируете переменную сокета и создаете ее прослушиватель каждый раз, когда принимается новое сообщение, вместо того, чтобы настраивать и инициализировать объект сокета только один раз в отдельном файле, а затем использовать его компонентом Workshop и, возможно, другими компонентами. через ваш проект.

  2. Вы добавляете вновь полученное сообщение, вызывая setState({ messages: [...] }) дважды в _onMessageWasSent .

Чтобы решить первую проблему, вы можете создать отдельный файл и перенести в него импорт и инициализацию, связанные с сокетом, например:

// socketHelper.js

import io from 'socket.io-client';

export const socket = io.connect('http://localhost:5000');

Помните, что это минимальная конфигурация, вы можете настроить объект сокета перед тем, как экспортировать его настолько, насколько позволяет API socket.io.


Затем, внутри componentDidMount компонента Workshop подписаться на этот объект сокета, как:

import { socket } from 'path/to/socketHelper.js';

class Workshop extends Component {
    constructor(props) {
        super(props);
        ...
    }

    componentDidMount() {
        // we subscribe to the imported socket object just once
        // by using arrow functions, no need to assign this to myThis
        socket.on('gmesssage', (data) => {
              this._onMessageWasSent(data);
        })  
    }

    ...
}

Реагировать на документы Сказать

Если вам нужно загрузить данные из удаленной конечной точки, это хорошее место для создания сетевого запроса.

componentDidMount будет запущен только один раз, поэтому ваш слушатель не будет переопределен несколько раз.


Чтобы решить вторую проблему, обработчик _onMessageWasSent должен получать только новое сообщение и добавлять его к предыдущим сообщениям с помощью setState, например:

_onMessageWasSent(data) {
    this.setState(previousState => ({
        messageList: [
            {
                author: 'them',
                type: 'text',
                data: data,
            }, 
            ...previousState.messageList,
        ]
    }))
}
3 голосов
/ 20 марта 2019

Так как вы уже используете redux, лучше сделать для socket.io диспетчерские действия при редукции для вас, вы можете использовать redux saga так же, как описано в этой статье , или вы можете использовать эту реализацию без saga, но вам нужно иметь redux-thunk middleware:

1 - Создать файл, скажем, lib/socket.js

затем внутри этого файла создайте socket.io клиент, который будет вызывать его socket, и создайте initSocketIO функцию, в которой вы можете запускать любые избыточные действия в ответ на socket.io:

 import socket_io from "socket.io-client"; 
 // our socket.io client
 export const socket = socket_io("http://localhost:5000");

 /**
 * init socket.io redux action 
 * @return {Function}
 */
export const initSocketIO = () => (dispatch, getState) => {
  // connect
  socket.on('connect', () => dispatch(appActions.socketConnected()));

  // disconnect
  socket.on('disconnect', () => dispatch(appActions.socketDisconnected()));

  // message
  socket.on('message', message => {
      dispatch(conversationActions.addOrUpdateMessage(message.conversationId, message.id, message));
      socket.emit("message-read", {conversationId: message.conversationId});
  });

  // message
  socket.on('notifications', ({totalUnread, notification}) => {
    dispatch(recentActions.addOrUpdateNotification(notification));
    dispatch(recentActions.setTotalUnreadNotifications(totalUnread));
  });
};

2 - тогда внутри вашего App компонента вызовите это действие, как только ваш App смонтирован:

import React, {Component} from "react";
import {initSocketIO} from "./lib/socket.js";

export class App extends Component {
   constructor(props) {
    super(props);
    this.store = configureStore();
  }
  componentDidMount() {
    // init socket io
    this.store.dispatch(initSocketIO());
  }
  // ... whatever
}

3 - теперь вы также можете запустить любое действие socket.io из любого компонента, используя это:

  import React, {Component} from "react";
  import {socket} from "./lib/socket.js"

  MyComponent extends Components {
     myMethod = () => socket.emit("message",{text: "message"});
  }

или из любого редукционного действия с использованием thunk, например:

  export const sendMessageAction = (text) => dispatch => {
     socket.emit("my message",{text});
     dispatch({type: "NEW_MESSAGE_SENT",payload:{text}});
  }

Примечания:

1 - это будет отлично работать, но все же я предпочитаю метод redux-saga для управления любыми побочными эффектами, такими как API и socket.io.

2 - В ваших компонентах вам не нужно связывать методы компонентов с this, просто используйте функции стрелок, например:

myMethod = () => {
  // you can use "this" here
}

render = (){
  return <Launcher onMessageWasSent={this.myMethod} />
}
1 голос
/ 20 марта 2019

В зависимости от вашего приложения, я думаю, что Redux будет излишним для этого.

Я бы здесь изменил лямбда-выражение, чтобы избежать необходимости сохранять следующие ссылки:

socket.on('gmesssage', data => {
  console.log(data);
  this.setState({
    messageList: [{
      author: 'them',
      type: 'text',
      data:  data
    }, ...this.messageList]
  })
})

Выможет рассмотреть возможность использования новых React Hooks, например, превратить его в компонент без состояния и использовать useState:

const Workshop = () => {
    const [messageList, setMessageList] = useState([]);
    ...
...