ashish@home:~$

Sharing Websocket In React Components

Note - This content in this post is a bit outdated. Check out the official docs for the Context API.

Often times there is a requirement to share a common websocket connection between React components. I have been using the React Context API to do that. Example for socket.io:

In this case there is a top level component file App.js where the socket is initialized and then made available to other React Components like HomePageComponent.js (which are its children) using Context.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React, { Component, PropTypes } from "react";
import io from "socket.io-client";

class App extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      message: null
    };
    this.socket = io("https://localhost:3001");
  }

  getChildContext() {
    return {
      socket: this.socket
    };
  }

  render() {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
}

App.propTypes = {
  children: PropTypes.object.isRequired
};

App.childContextTypes = {
  socket: PropTypes.object.isRequired
};

export default App;

A common pattern while using Context is to write a Provider for the children. In that case, after initializing the websocket in App.js, any other component(s) can be provided access to the websocket via its props by wrapping it in the provider like this:

1
2
3
4
5
6
7
8
render() {
  return (
    <SocketProvider>
      <MyComponent1 />
      <MyComponent2 />
    </SocketProvider> 
  );
}

Here the SocketProvider is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component, PropTypes } from "react";

class SocketProvider extends Component {
  constructor(props, context) {
    super(props, context);
    this.socket = context.socket;
  }

  render() {
    return (
      <span>
        {React.cloneElement(this.props.children, {
          ...this.props,
          ...{ socket: this.socket }
        })}
      </span>
    );
  }
}

SocketProvider.contextTypes = {
  socket: PropTypes.object.isRequired
};

export default SocketProvider;

and is accessible in MyComponent1 and MyComponent2 as this.props.socket. Note that this SocketProvider only provides the websocket one level deep - children of MyComponent1 do not get to use the websocket unless they are wrapped in SocketProvider themsevles. Also note that {...this.props, { socket: this.socket }} is a shallow merge.