如何使用 Context API 将单个 Socket IO 实例传递给页面?
Posted
技术标签:
【中文标题】如何使用 Context API 将单个 Socket IO 实例传递给页面?【英文标题】:How to pass single Socket IO Instance to pages with Context API? 【发布时间】:2019-12-02 10:22:23 【问题描述】:所以我试图在整个应用程序中使用一个 Socket IO 实例并从多个组件访问它。
我已经搜索了很多,也尝试了很多......没有任何成功。
现在我正在使用 React Context API。
我在SocketProvider.js
中初始化我的SocketContext
:
import React from 'react'
const SocketContext = React.createContext()
export default SocketContext
打开 Socket 并在App.js
中提供它:
import React from 'react';
import Main from './components/Main/Main';
import io from 'socket.io-client';
import SocketContext from './services/SocketProvider'
function App()
const socket = io("http://localhost:8000")
return (
<div className="App">
<SocketContext.Provider value=socket>
<MuiPickersUtilsProvider>
<MuiThemeProvider >
<Main />
</MuiThemeProvider>
</MuiPickersUtilsProvider>
</SocketContext.Provider>
</div>
);
export default App;
Main.js
中的路由:
function Main()
return (
<main>
<Switch>
<Route exact path="/" component=Menu />
<Route path="/panorama" component=Panorama />
<Route path="/movie" component=Movie />
<Route
path="/timelapse"
render=( match: url ) => (
<>
<Route path=`$url/` component=Timelapse exact />
<Route path=`$url/running` render=(props) => <Running ...props type='timelapse' /> />
</>
)
/>
</Switch>
</main>
);
export default Main;
并在这两个组件中使用它,Timelapse.js
:
import React from 'react';
import SocketContext from '../../services/SocketProvider';
import TimelapsePoints from '../../components/TimelapsePoints/TimelapsePoints';
import TimelapseCamControls from '../../components/TimelapseCamControls/TimelapseCamControls'
class Timelapse extends React.Component
constructor(props)
super(props);
let socket = props.socket;
this.state =
hasCamera: false,
cameraActive: false,
brightnessControl: false,
camControlsOk: false,
pointsOk: false,
interval: 10,
recordingTime: 6000,
movieTime: 12
;
socket.on('hasCamera', data =>
this.setState(
cameraActive: data.hasCamera
)
this.setState(
hasCamera: data.hasCamera
)
)
render()
return (<div >
this.state.cameraActive &&
<TimelapseCamControls brightnessControl=
this.state.brightnessControl
toggleBrightnesscontrol=
this.toggleBrightnesscontrol
camControlsOk=
this.camControlsOk
socket=
this.props.socket
/>
(!this.state.brightnessControl || (this.state.brightnessControl && this.state.camControlsOk)) &&
<TimelapsePoints socket=this.props.socket handlePointsChange=this.pointsOk/>
this.state.pointsOk && this.camControlsOk &&
<Button variant="outlined" onClick=this.timelapse href="/timelapse/running">
Start
</Button>
</div>
);
const TimelapseWithSocket = (props) => (
<SocketContext.Consumer>
socket => <Timelapse ...props socket=socket />
</SocketContext.Consumer>
)
export default TimelapseWithSocket;
和Running.js
:
import React from 'react';
import SocketContext from '../../services/SocketProvider';
class Running extends React.Component
constructor(props)
super(props);
this.state =
progress: 0,
maxProgress: 100,
waypoints: []
;
componentDidMount()
this.props.socket.on('progress', data =>
this.setState(
progress: data.value,
maxProgress: data.max
)
)
this.props.socket.on('timelapseInfo', data =>
console.log('recived date');
this.setState(
waypoints: data.waypoints,
maxProgress: data.max
)
)
console.log("component mounte");
render()
var type = this.props.type;
var title = type.charAt(0).toUpperCase() + type.slice(1)
return (<div >
</div>
);
const RunningWithSocket = (props) => (
<SocketContext.Consumer>
socket => <Running ...props socket=socket />
</SocketContext.Consumer>
)
export default RunningWithSocket;
但是当我到达/timelapse/running
的路线时,我得到了一个新的 Socket 实例,但我不知道为什么以及如何修复它。
感谢您的帮助:)
【问题讨论】:
【参考方案1】:要达到相同的结果,您不需要 Context API,只需从模块中导出 websocket 连接:
// ws.js
import io from 'socket.io-client'
const ws = io.connect(<host>)
export ws
然后你可以在你的组件中使用 useEffect(fn, []) 来添加事件,两个方括号作为第二个参数用于使效果只运行一次:
// wsComponent.js
import React, useState, useEffect from 'react'
import Text from 'react-native'
import ws from 'ws.js'
const WsComponent = () =>
const [wsState, updateWsState] = useState(false)
useEffect(() =>
ws.on('connect', () =>
updateWsState(true)
)
ws.on('disconnect', () =>
updateWsState(false)
)
, [])
return (
<Text>wsState ? 'ONLINE' : 'OFFLINE'</Text>
)
export WsComponent
对不起,如果我在代码中犯了错误,我没有测试它。
【讨论】:
socket 在 useEffect 中为空,并且将 socket 添加到相同 useEffect 的依赖数组中没有帮助...【参考方案2】:这可能有助于使您的 App 组件成为纯组件和/或将您的套接字提供程序提取到它自己的纯组件中以避免重新渲染,这可能会导致实例发生变化。或者,您可以尝试将您的 socket.io 包包装在单例模式中。
【讨论】:
在Timelapse.js
中使用href
可能会出现问题,这可能会导致App
的重新加载?【参考方案3】:
错误是在我的 Button 上使用 href
更改路由时使用了 Link
组件,这不会触发应用程序的完全重新加载,因此会初始化一个新的 Socket IO 实例。
所以不要使用:
<Button variant="outlined" onClick=this.timelapse href="/timelapse/running">Start</Button>
我不得不使用:
<Button variant="outlined" onClick=this.timelapse component= Link to="/timelapse/running">Start</Button>
希望这可以为遇到相同问题的人提供解决方案。
【讨论】:
【参考方案4】:// create a socket.js where you export the client socket created by io(...)
const io = require('socket.io-client')
module.exports = io('http://localhost:5000') //your port
//when you need to use the socket just import and enjoy
import socket from './socket'
socket.on('some_event',()=> console.log('do something') )
【讨论】:
以上是关于如何使用 Context API 将单个 Socket IO 实例传递给页面?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 React Context API 与 useReducer 一起使用以遵循与 Redux 类似的风格 [关闭]
如何使用 React Context API 跨多个 Routes 拥有状态?
如何使用 React Hooks 和 Context API 正确地将来自 useEffect 内部调用的多个端点的数据添加到状态对象?
React Context API + withRouter - 我们可以一起使用它们吗?
在 React.js 的父组件中使用 react-router 时,如何使用 react context API 将数据从父组件传递到子组件?