|
1 | 1 | // @flow strict-local
|
2 |
| -import { PureComponent } from 'react'; |
3 |
| -import type { ComponentType } from 'react'; |
| 2 | +import * as React from 'react'; |
4 | 3 | import { AppState } from 'react-native';
|
5 | 4 |
|
6 |
| -import { assumeSecretlyGlobalState, type Dispatch } from '../reduxTypes'; |
7 |
| -import { connect } from '../react-redux'; |
| 5 | +import { useGlobalSelector, useDispatch } from '../react-redux'; |
8 | 6 | import { getHasAuth } from '../account/accountsSelectors';
|
9 | 7 | import { reportPresence } from '../actions';
|
10 | 8 | import Heartbeat from './heartbeat';
|
11 | 9 |
|
12 |
| -type OuterProps = $ReadOnly<{||}>; |
13 |
| - |
14 |
| -type SelectorProps = $ReadOnly<{| |
15 |
| - hasAuth: boolean, |
16 |
| -|}>; |
17 |
| - |
18 |
| -type Props = $ReadOnly<{| |
19 |
| - ...OuterProps, |
20 |
| - |
21 |
| - // from `connect` |
22 |
| - dispatch: Dispatch, |
23 |
| - ...SelectorProps, |
24 |
| -|}>; |
| 10 | +type Props = $ReadOnly<{||}>; |
25 | 11 |
|
26 | 12 | /**
|
27 | 13 | * Component providing a recurrent `presence` signal.
|
28 | 14 | */
|
29 |
| -// The name "PureComponent" is potentially misleading here, as this component is |
30 |
| -// in no reasonable sense "pure" -- its entire purpose is to emit network calls |
31 |
| -// for their observable side effects as a side effect of being rendered. |
32 |
| -// |
33 |
| -// (This is merely a misnomer on React's part, rather than a functional error. A |
34 |
| -// `PureComponent` is simply one which never updates except when its props have |
35 |
| -// changed -- which is exactly what we want.) |
36 |
| -class PresenceHeartbeatInner extends PureComponent<Props> { |
37 |
| - /** Callback for Heartbeat object. */ |
38 |
| - onHeartbeat = () => { |
39 |
| - if (this.props.hasAuth) { |
40 |
| - // TODO(#5005): should ensure this gets the intended account |
41 |
| - this.props.dispatch(reportPresence(true)); |
42 |
| - } |
43 |
| - }; |
44 |
| - |
45 |
| - heartbeat: Heartbeat = new Heartbeat(this.onHeartbeat, 1000 * 60); |
46 |
| - |
47 |
| - componentDidMount() { |
48 |
| - AppState.addEventListener('change', this.updateHeartbeatState); |
49 |
| - this.updateHeartbeatState(); // conditional start |
50 |
| - } |
51 |
| - |
52 |
| - componentWillUnmount() { |
53 |
| - AppState.removeEventListener('change', this.updateHeartbeatState); |
54 |
| - this.heartbeat.stop(); // unconditional stop |
55 |
| - } |
56 |
| - |
57 |
| - // React to any state change. |
58 |
| - updateHeartbeatState = () => { |
59 |
| - // heartbeat.toState is idempotent |
60 |
| - this.heartbeat.toState(AppState.currentState === 'active' && this.props.hasAuth); |
61 |
| - }; |
| 15 | +// TODO(#5005): either make one of these per account, or make it act on all accounts |
| 16 | +export default function PresenceHeartbeat(props: Props): React.Node { |
| 17 | + const dispatch = useDispatch(); |
| 18 | + const hasAuth = useGlobalSelector(getHasAuth); // a job for withHaveServerDataGate? |
62 | 19 |
|
63 |
| - // React to props changes. |
64 |
| - // |
65 |
| - // Not dependent on `render()`'s return value, although the docs may not yet |
66 |
| - // be clear on that. See: https://github.com/reactjs/reactjs.org/pull/1230. |
67 |
| - componentDidUpdate() { |
68 |
| - this.updateHeartbeatState(); |
69 |
| - } |
| 20 | + React.useEffect(() => { |
| 21 | + if (!hasAuth) { |
| 22 | + return; |
| 23 | + } |
70 | 24 |
|
71 |
| - render() { |
72 |
| - return null; |
73 |
| - } |
| 25 | + const onHeartbeat = () => { |
| 26 | + // TODO(#5005): should ensure this gets the intended account |
| 27 | + dispatch(reportPresence(true)); |
| 28 | + }; |
| 29 | + const heartbeat = new Heartbeat(onHeartbeat, 1000 * 60); |
| 30 | + |
| 31 | + // React to any state change. |
| 32 | + const updateHeartbeatState = () => { |
| 33 | + // heartbeat.toState is idempotent |
| 34 | + heartbeat.toState(AppState.currentState === 'active'); |
| 35 | + }; |
| 36 | + |
| 37 | + const sub = AppState.addEventListener('change', updateHeartbeatState); |
| 38 | + updateHeartbeatState(); // conditional start |
| 39 | + |
| 40 | + return () => { |
| 41 | + sub.remove(); |
| 42 | + heartbeat.stop(); // unconditional stop |
| 43 | + }; |
| 44 | + }, [dispatch, hasAuth]); |
| 45 | + |
| 46 | + return null; |
74 | 47 | }
|
75 |
| - |
76 |
| -/** (NB this is a per-account component.) */ |
77 |
| -// TODO(#5005): either make one of these per account, or make it act on all accounts |
78 |
| -const PresenceHeartbeat: ComponentType<OuterProps> = connect(state => ({ |
79 |
| - hasAuth: getHasAuth(assumeSecretlyGlobalState(state)), // a job for withHaveServerDataGate? |
80 |
| -}))(PresenceHeartbeatInner); |
81 |
| - |
82 |
| -export default PresenceHeartbeat; |
0 commit comments