如何在 Elixir Phoenix 中获取频道消息以更改 React 组件的状态?

How to get channel messages in Elixir Phoenix to change the state of a React component?

我正在尝试弄清楚如何根据外部事件更新组件的状态,在这种情况下,外部事件是来自 Elixir Phoenix 频道的消息。

基本上,我有一个简单的 h1 标签,它必须始终反映频道中的最新内容。所以有两个相互关联的问题:

a) 如何将频道导入组件?到目前为止,我已经通过将频道作为道具传递来完成它。

b) 如何处理进入组件内部通道的消息?我的 "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. 

函数外的 class 主体中不能有 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);
  }

  ...
}