Я уже некоторое время работаю над вашим вопросом, и вот мои выводы:
Эмиссия событий работает просто отлично
Я восстановил ваш бэкэнд следующим образом:
const express = require('express')
const app = express()
const http = require('http').createServer(app)
const io = require('socket.io')(http)
const port = 3003
app.use(express.json())
app.get('/emit', async({ res }) => {
const [data1, data2] = await Promise.all([fakeApiCall(1), fakeApiCall(2)])
io.emit('Emit1',data1)
io.emit('Emit2',data2)
res.status(200).json('emitted')
})
const fakeApiCall = id =>{
return new Promise((resolve, reject) =>{
setTimeout(() => resolve(`data${id}`), 3000)
})
}
http.listen(port, () => console.log('listening on port ' + port))
По сути, это именно то, что вы делаете, и работает отлично, оба события выдают после того, как Promise.all
выполнит все обещания.
Настройка внешнего интерфейса работает
После этого я сделал версию вашего интерфейса:
App.js:
import React from 'react'
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
const App = () => {
return (
<>
<ComponentA />
<ComponentB />
</>
)
}
export default App
Компонент А:
import React from 'react'
import socketIOClient from 'socket.io-client'
class ComponentA extends React.Component {
state = {
data: null
}
componentDidMount(){
setTimeout(() => this.setState({data: 'bla'}), 2000)
}
componentDidUpdate(prevProps, prevState) {
const endpoint = 'http://127.0.0.1:3003';
if (prevState.data !== this.state.data) {
const socket = socketIOClient(endpoint);
socket.on("Emit1", data => this.setState({ data }))
}
}
render() {
const { data } = this.state
return (
<div>
{data}
</div>
)
}
}
export default ComponentA
Компонент B:
import React from 'react'
import socketIOClient from 'socket.io-client'
class ComponentB extends React.Component {
state = {
data: null
}
componentDidMount(){
setTimeout(() => this.setState({data: 'bla'}), 2000)
}
componentDidUpdate(prevProps, prevState) {
const endpoint = 'http://127.0.0.1:3003';
if (prevState.data !== this.state.data) {
const socket = socketIOClient(endpoint);
socket.on("Emit2", data => this.setState({ data }))
}
}
render() {
const { data } = this.state
return (
<div>
{data}
</div>
)
}
}
export default ComponentB
И что удивительно ... Работает просто отлично! Итак, в чем здесь разница? Все выглядит одинаково (как генерируются события, как слушатель вставляется только в componentDidUpdate
, как изменяется состояние после асинхронного вызова в componentDidMount
).
Таким образом, единственное, что может помешать, это вызов axios, который вы делаете внутри componentDidMount
, потому что в обоих компонентах io.listen
не установлен на componentDidMount
, как это должно быть (я полагаю, у вас есть причины для этого), поэтому оба компонента ДОЛЖНЫ обновить состояние до того, как событие будет выпущено, в противном случае, если свойства data
или abcd
не будут обновлены вовремя, прослушиватель событий будет вызван после того, как бэкэнд испустит данные. И ваше утверждение о том, что если вы пропустите Emit2
Emit1
, будет работать нормально. Но как насчет опустить Emit1
? Emit2
тоже будет работать? Похоже на состояние гонки, что-то в componentA
componentDidMount
мешает componentB
обновлять или наоборот
Возможное решение
Поместите слушателей io в componentDidMount
в обоих компонентах, чтобы определить, так ли это. Потому что, честно говоря, я не мог думать ни о какой другой проблеме, которая может быть причиной этого.