So I'm trying to use one single Socket IO instance in my entire App and access it from multiple components.
I searched a lot already and tried a lot too... without any success.
Right now Im using the React Context API.
Im initialising my SocketContext
in SocketProvider.js
:
import React from 'react'
const SocketContext = React.createContext()
export default SocketContext
Opening the Socket and Providing it in 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;
Routing in 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;
And using it in those two Components, 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;
and 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;
But as soon as I hit the route /timelapse/running
I get a new Socket Instance and I don't know why and how to fix it.
Thanks for any help :)
To achieve the same result you don't need the Context API but simply export the websocket connection from a module:
// ws.js
import io from 'socket.io-client'
const ws = io.connect(<host>)
export { ws }
Then you can use useEffect(fn, []) in your components to add events, the two square brackets as a second parameter are used to make the effect run only once:
// 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 }
sorry if I made mistakes in the code, I didn't test it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With