Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass single Socket IO Instance to pages with Context API?

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 :)

like image 345
FabZbi Avatar asked Jul 24 '19 09:07

FabZbi


1 Answers

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.

like image 138
Alessandro Bottamedi Avatar answered Nov 15 '22 00:11

Alessandro Bottamedi