Как получить сообщения канала в Elixir Phoenix для изменения состояния компонента React?

Я пытаюсь выяснить, как обновить состояние компонента на основе внешнего события, и в этом случае внешнее событие - это сообщение, поступающее по каналу Elixir Phoenix.

По сути, у меня есть простой тег h1, и он всегда должен отражать последние новости, поступающие в канал. Итак, есть два взаимосвязанных вопроса:

а) как мне вставить канал в компонент? Пока я делал это, передавая канал как опору.

б) Как мне обрабатывать сообщения, поступающие в канал внутри компонента? Мой "this.state.chan.on" не работает и кажется неуклюжим.

import socket from "./socket"
import React from "react"
import ReactDOM from "react-dom"

socket.connect()

// Now that you are connected, you can join channels with a topic:
let channel = socket.channel("topic:subtopic", {})

channel.join()
  .receive("ok", resp => { console.log("Joined successfully", resp) })
  .receive("error", resp => { console.log("Unable to join", resp) })

class HelloWorld extends React.Component {
  state = {
    chan: this.props.channel,
    mess: this.props.message
  }

  this.state.chan.on("new_message", payload => {
    this.setState(prevstate => {
      return {mess: ${payload.body}}
    });
  })


  componentDidMount = () => {
    console.log("did mount Hello World")
  }

  render = () => {
    return (<h1>{this.state.mess}</h1>)
  }
}


ReactDOM.render(
  <HelloWorld message={1} channel={channel}/>,
  document.getElementById("hello-world")
)

Как это принято делать? Как мне получить сообщения из канала или сокета или чего-то еще, сгенерированные вне реакции и за пределами пользовательского интерфейса, чтобы повлиять на состояние компонентов и связанных с этим, как мне в первую очередь передать внешнее событие в компонент? Правильно ли ставить канал в компонент? Потому что это также, похоже, ограничивает вывод канала воздействием только на этот компонент, а не на другие независимые компоненты, на которые я мог бы захотеть повлиять.

РЕДАКТИРОВАТЬ: вот сообщение об ошибке компиляции. Да, я понимаю, что мой JS может быть неправильным, но я получаю синтаксическую ошибку прямо здесь, на первом this.state.chan.on:

17:55:13 - error: Compiling of web/static/js/app.js failed. L40:6 Unexpected token 
     38 |   }
     39 | 
   > 40 |   this.state.chan.on(
        |       ^
     41 | 
     42 |   componentDidMount = () => {
     43 |     console.log("did mount Hello World")
Stack trace was suppressed. Run with `LOGGY_STACKS=1` to see the trace. 
18:07:20 - error: Compiling of web/static/js/app.js failed. L40:6 Unexpected token 
     38 |   }
     39 | 
   > 40 |   this.state.chan.on("new_message", payload => {
        |       ^
     41 |     this.setState(prevstate => {
     42 |       return {mess: ${payload.body}}
     43 |     });
Stack trace was suppressed. Run with `LOGGY_STACKS=1` to see the trace. 
18:07:22 - error: Compiling of web/static/js/app.js failed. L40:6 Unexpected token 
     38 |   }
     39 | 
   > 40 |   this.state.chan.on("new_message", payload => {
        |       ^
     41 |     this.setState(prevstate => {
     42 |       return {mess: ${payload.body}}
     43 |     });
Stack trace was suppressed. Run with `LOGGY_STACKS=1` to see the trace. 

person Thomas Browne    schedule 22.08.2017    source источник
comment
My "this.state.chan.on" doesn't work Какая часть не работает? Выдает ошибку? Он вообще не вызывается?   -  person Dogbert    schedule 22.08.2017
comment
this.setState(prevstate => { return {mess: ${payload.body}} }); это похоже на синтаксическую ошибку.   -  person Dogbert    schedule 22.08.2017
comment
Отредактировано, чтобы отразить два вышеупомянутых комментария. Прошу прощения за мой плохой JS, да, у меня может быть синтаксическая ошибка, но, похоже, проблема возникла еще до этого ....   -  person Thomas Browne    schedule 22.08.2017


Ответы (1)


Вы не можете иметь this.state.chan.on(...) в теле класса вне функций. Однако вы можете поместить весь этот код в конструктор. Кроме того, ваш setState вызов содержит синтаксическую ошибку, и его можно упростить, чтобы использовать объект в качестве аргумента. Вот как будет выглядеть конструктор:

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

    this.state = {
      chan: props.channel,
      mess: props.message
    };

    this.state.chan.on("new_message", payload => {
      this.setState({mess: payload.body});
    });
  }

  ...
}

Однако с этим есть одна проблема. Обратный вызов on будет продолжать запускаться, даже если этот компонент отключен от DOM. Вы должны подписаться на сообщения в componentDidMount и отказаться от подписки в componentWillUnmount:

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

    this.state = {
      chan: props.channel,
      mess: props.message
    };
  }

  componentDidMount() {
    const ref = this.state.chan.on("new_message", payload => {
      this.setState({mess: payload.body});
    });
    this.setState({ref: ref});
  }

  componentWillUnmount() {
    this.state.chan.off("new_message", this.state.ref);
  }

  ...
}
person Dogbert    schedule 22.08.2017
comment
setState (oldstate = ›newState) действительно действительно - person webdeb; 22.08.2017
comment
@webdeb да, эта часть действительна, но return {mess: ${payload.body}} является синтаксической ошибкой. - person Dogbert; 22.08.2017
comment
интересно, и я не знаю, это я, но я получаю undefined в this.state.ref. Я немного изменил ваш код: componentDidMount () {console.log (this.state); const ref = this.state.chan.on (counter, payload = ›{console.log (this.state); let thebody = payload.counter; this.setState ({mess: thebody}); this.state.chan. push (counter, {body: thebody})}); this.setState ({ref: ref}); this.state.chan.push (counter, {body: 1})} - person Thomas Browne; 23.08.2017
comment
Итак, я отправляю счетчик обратно в канал, и канал возвращает его мне с приращением на единицу. Обновления работают нормально - т.е. react обновляет дом. Мне просто интересно, почему в моей версии this.state.ref не определен. - person Thomas Browne; 23.08.2017
comment
В какой момент this.state.ref не определено? В componentWillUnmount? - person Dogbert; 23.08.2017
comment
Внутри компонента я делаю console.log (this.state), и там он не определен. Имеет ли это смысл? возможно, это не undefined, когда мы дойдем до componentWillUnmount? Apols Я научный программист, привыкший к императивному стилю, и правила потока управления js и правила области видимости все еще для меня в новинку. - person Thomas Browne; 23.08.2017