diff --git a/lib/socket/api-request-handler.react.js b/lib/socket/api-request-handler.react.js index 11c3cdfc4..9a3172270 100644 --- a/lib/socket/api-request-handler.react.js +++ b/lib/socket/api-request-handler.react.js @@ -1,101 +1,97 @@ // @flow import invariant from 'invariant'; -import PropTypes from 'prop-types'; import * as React from 'react'; import type { APIRequest } from '../types/endpoints'; -import type { BaseAppState } from '../types/redux-types'; import { clientSocketMessageTypes, serverSocketMessageTypes, type ClientSocketMessageWithoutID, type ConnectionInfo, - connectionInfoPropType, } from '../types/socket-types'; import { registerActiveSocket } from '../utils/action-utils'; -import { connect } from '../utils/redux-utils'; +import { useSelector } from '../utils/redux-utils'; import { InflightRequests, SocketOffline } from './inflight-requests'; +type BaseProps = {| + +inflightRequests: ?InflightRequests, + +sendMessage: (message: ClientSocketMessageWithoutID) => number, +|}; type Props = {| - inflightRequests: ?InflightRequests, - sendMessage: (message: ClientSocketMessageWithoutID) => number, - // Redux state - connection: ConnectionInfo, + ...BaseProps, + +connection: ConnectionInfo, |}; class APIRequestHandler extends React.PureComponent { - static propTypes = { - inflightRequests: PropTypes.object, - sendMessage: PropTypes.func.isRequired, - connection: connectionInfoPropType.isRequired, - }; - static isConnected(props: Props, request?: APIRequest) { const { inflightRequests, connection } = props; if (!inflightRequests) { return false; } // This is a hack. We actually have a race condition between // ActivityHandler and Socket. Both of them respond to a backgrounding, but // we want ActivityHandler to go first. Once it sends its message, Socket // will wait for the response before shutting down. But if Socket starts // shutting down first, we'll have a problem. Note that this approach only // stops the race in fetchResponse below, and not in action-utils (which // happens earlier via the registerActiveSocket call below), but empircally // that hasn't been an issue. // The reason I didn't rewrite this to happen in a single component is // because I want to maintain separation of concerns. Upcoming React Hooks // will be a great way to rewrite them to be related but still separated. return ( connection.status === 'connected' || (request && request.endpoint === 'update_activity') ); } get registeredResponseFetcher() { return APIRequestHandler.isConnected(this.props) ? this.fetchResponse : null; } componentDidMount() { registerActiveSocket(this.registeredResponseFetcher); } componentWillUnmount() { registerActiveSocket(null); } componentDidUpdate(prevProps: Props) { const isConnected = APIRequestHandler.isConnected(this.props); const wasConnected = APIRequestHandler.isConnected(prevProps); if (isConnected !== wasConnected) { registerActiveSocket(this.registeredResponseFetcher); } } render() { return null; } fetchResponse = async (request: APIRequest): Promise => { if (!APIRequestHandler.isConnected(this.props, request)) { throw new SocketOffline('socket_offline'); } const { inflightRequests } = this.props; invariant(inflightRequests, 'inflightRequests falsey inside fetchResponse'); const messageID = this.props.sendMessage({ type: clientSocketMessageTypes.API_REQUEST, payload: request, }); const response = await inflightRequests.fetchResponse( messageID, serverSocketMessageTypes.API_RESPONSE, ); return response.payload; }; } -export default connect((state: BaseAppState<*>) => ({ - connection: state.connection, -}))(APIRequestHandler); +export default React.memo(function ConnectedAPIRequestHandler( + props: BaseProps, +) { + const connection = useSelector((state) => state.connection); + return ; +});