Refactor connection reducer & tree

This commit is contained in:
Thomas Nordquist
2019-01-20 22:34:15 +01:00
parent ecd3a23311
commit ba4efbe30d
10 changed files with 163 additions and 107 deletions

View File

@@ -87,7 +87,7 @@ class App extends React.Component<Props, {}> {
<TitleBar />
<div style={centerContent}>
<div style={this.getStyles().left}>
<Tree connectionId={this.props.connectionId} />
<Tree />
</div>
<div style={this.getStyles().right}>
<Sidebar connectionId={this.props.connectionId} />
@@ -105,7 +105,7 @@ class App extends React.Component<Props, {}> {
const mapStateToProps = (state: AppState) => {
return {
settingsVisible: state.tooBigReducer.settings.visible,
connectionId: state.tooBigReducer.connectionId,
connectionId: state.connection.connectionId,
}
}

View File

@@ -1,7 +1,9 @@
import { ActionTypes, NodeOrder, CustomAction, AppState } from '../reducers'
import { ActionTypes, Action, ConnectionState } from '../reducers/Connection'
import { MqttOptions } from '../../../backend/src/DataSource'
import { Dispatch } from 'redux'
import { rendererEvents, addMqttConnectionEvent, makeConnectionStateEvent, removeConnection } from '../../../events'
import { AppState } from '../reducers'
import * as q from '../../../backend/src/Model'
export const connect = (options: MqttOptions, connectionId: string) => (dispatch: Dispatch<any>, getState: () => AppState) => {
dispatch(connecting(connectionId))
@@ -9,7 +11,9 @@ export const connect = (options: MqttOptions, connectionId: string) => (dispatch
const event = makeConnectionStateEvent(connectionId)
rendererEvents.subscribe(event, (dataSourceState) => {
if (dataSourceState.connected) {
dispatch(connected())
const tree = new q.Tree()
tree.updateWithConnection(rendererEvents, connectionId)
dispatch(connected(tree))
} else if (dataSourceState.error) {
dispatch(showError(dataSourceState.error))
dispatch(disconnect())
@@ -17,24 +21,27 @@ export const connect = (options: MqttOptions, connectionId: string) => (dispatch
})
}
export const connected: () => CustomAction = () => ({
type: ActionTypes.connected,
export const connected: (tree: q.Tree) => Action = (tree: q.Tree) => ({
tree,
type: ActionTypes.CONNECTION_SET_CONNECTED,
})
export const connecting: (connectionId: string) => CustomAction = (connectionId: string) => ({
export const connecting: (connectionId: string) => Action = (connectionId: string) => ({
connectionId,
type: ActionTypes.connecting,
type: ActionTypes.CONNECTION_SET_CONNECTING,
})
export const showError = (error?: string) => ({
error,
type: ActionTypes.showError,
type: ActionTypes.CONNECTION_SET_SHOW_ERROR,
})
export const disconnect = () => (dispatch: Dispatch<CustomAction>, getState: () => AppState) => {
rendererEvents.emit(removeConnection, getState().tooBigReducer.connectionId)
export const disconnect = () => (dispatch: Dispatch<Action>, getState: () => AppState) => {
const { connectionId, tree } = getState().connection
rendererEvents.emit(removeConnection, connectionId)
tree && tree.stopUpdating()
dispatch({
type: ActionTypes.disconnect,
type: ActionTypes.CONNECTION_SET_DISCONNECTED,
})
}

View File

@@ -3,7 +3,9 @@ import { AppState } from '../reducers'
import { makePublishEvent, rendererEvents } from '../../../events'
export const clearRetainedTopic = () => (dispatch: Dispatch<Action>, getState: () => AppState) => {
const { selectedTopic, connectionId } = getState().tooBigReducer
const { selectedTopic } = getState().tooBigReducer
const { connectionId } = getState().connection
if (!selectedTopic || !connectionId) {
return
}

View File

@@ -380,10 +380,10 @@ class Connection extends React.Component<Props, State> {
const mapStateToProps = (state: AppState) => {
return {
visible: !state.tooBigReducer.connected,
connected: state.tooBigReducer.connected,
connecting: state.tooBigReducer.connecting,
error: state.tooBigReducer.error,
visible: !state.connection.connected,
connected: state.connection.connected,
connecting: state.connection.connecting,
error: state.connection.error,
}
}

View File

@@ -1,5 +1,4 @@
import * as React from 'react'
import * as q from '../../../../backend/src/Model'
import { Badge, Typography } from '@material-ui/core'
import { Theme, withStyles } from '@material-ui/core/styles'

View File

@@ -18,22 +18,17 @@ interface Props {
autoExpandLimit: number
didSelectNode?: (node: q.TreeNode) => void
connectionId?: string
connected: boolean
tree?: q.Tree
}
interface TreeState {
tree: q.Tree
msg: any
}
class Tree extends React.Component<Props, TreeState> {
class Tree extends React.Component<Props, {}> {
private updateTimer?: any
private lastUpdate: number = 0
private perf: number = 0
constructor(props: any) {
super(props)
this.state = { tree: new q.Tree(), msg: {} }
this.state = { }
}
public time(): number {
@@ -43,11 +38,18 @@ class Tree extends React.Component<Props, TreeState> {
return time
}
private performanceCallback = (ms: number) => {
average.push(Date.now(), ms)
public componentWillReceiveProps(nextProps: Props) {
if (this.props.tree !== nextProps.tree) {
if (this.props.tree) {
this.props.tree.onMerge.unsubscribe(this.throttledTreeUpdate)
}
if (nextProps.tree) {
nextProps.tree.onMerge.subscribe(this.throttledTreeUpdate)
}
}
}
public throttledTreeUpdate() {
public throttledTreeUpdate = () => {
if (this.updateTimer) {
return
}
@@ -66,23 +68,6 @@ class Tree extends React.Component<Props, TreeState> {
}, Math.max(0, timeUntilNextUpdate))
}
public componentWillReceiveProps(nextProps: Props) {
this.registerAndUnregisterEventSubscriptionsForNewProps(nextProps)
}
private registerAndUnregisterEventSubscriptionsForNewProps(nextProps: Props) {
if (this.props.connectionId !== nextProps.connectionId) {
if (this.props.connectionId) {
this.setState({ tree: new q.Tree() })
rendererEvents.unsubscribeAll(makeConnectionMessageEvent(this.props.connectionId))
this.updateTimer && clearTimeout(this.updateTimer)
}
if (nextProps.connectionId) {
rendererEvents.subscribe(makeConnectionMessageEvent(nextProps.connectionId), this.handleNewData)
}
}
}
public componentWillUnmount() {
if (this.props.connectionId) {
const event = makeConnectionMessageEvent(this.props.connectionId)
@@ -90,17 +75,8 @@ class Tree extends React.Component<Props, TreeState> {
}
}
private handleNewData = (msg: MqttMessage) => {
const edges = msg.topic.split('/')
const node = q.TreeNodeFactory.fromEdgesAndValue(edges, msg.payload)
node.mqttMessage = msg
this.state.tree.updateWithNode(node.firstNode())
this.throttledTreeUpdate()
}
public render() {
if (!this.props.connected) {
if (!this.props.tree) {
return null
}
@@ -116,22 +92,26 @@ class Tree extends React.Component<Props, TreeState> {
autoExpandLimit={this.props.autoExpandLimit}
isRoot={true}
didSelectNode={this.props.didSelectNode}
treeNode={this.state.tree}
treeNode={this.props.tree}
name="/"
collapsed={false}
key="rootNode"
lastUpdate={this.state.tree.lastUpdate}
lastUpdate={this.props.tree.lastUpdate}
performanceCallback={this.performanceCallback}
/>
</div>
)
}
private performanceCallback = (ms: number) => {
average.push(Date.now(), ms)
}
}
const mapStateToProps = (state: AppState) => {
return {
autoExpandLimit: state.tooBigReducer.settings.autoExpandLimit,
connected: state.tooBigReducer.connected,
tree: state.connection.tree,
}
}

View File

@@ -0,0 +1,88 @@
import { Action } from 'redux'
import { createReducer } from './lib'
import * as q from '../../../backend/src/Model'
import { MqttOptions } from '../../../backend/src/DataSource'
export interface ConnectionState {
tree?: q.Tree
connectionOptions?: MqttOptions
connectionId?: string
error?: string
connected: boolean
connecting: boolean
}
export type Action = SetConnecting | SetConnected | SetDisconnected | ShowError
export enum ActionTypes {
CONNECTION_SET_CONNECTING = 'CONNECTION_SET_CONNECTING',
CONNECTION_SET_CONNECTED = 'CONNECTION_SET_CONNECTED',
CONNECTION_SET_DISCONNECTED = 'CONNECTION_SET_DISCONNECTED',
CONNECTION_SET_SHOW_ERROR = 'CONNECTION_SET_SHOW_ERROR',
}
export interface SetConnecting {
type: ActionTypes.CONNECTION_SET_CONNECTING,
connectionId: string
}
export interface SetConnected {
type: ActionTypes.CONNECTION_SET_CONNECTED
tree: q.Tree
}
export interface SetDisconnected {
type: ActionTypes.CONNECTION_SET_DISCONNECTED
}
export interface ShowError {
type: ActionTypes.CONNECTION_SET_SHOW_ERROR
error?: Error
}
const initialState: ConnectionState = {
connected: false,
connecting: false,
}
export const connectionReducer = createReducer(initialState, {
CONNECTION_SET_CONNECTING: setConnecting,
CONNECTION_SET_CONNECTED: setConnected,
CONNECTION_SET_DISCONNECTED: setDisconnected,
CONNECTION_SET_SHOW_ERROR: showError,
})
function setConnecting(state: ConnectionState, action: SetConnecting) {
return {
...state,
connecting: true,
connected: false,
connectionId: action.connectionId,
}
}
function setConnected(state: ConnectionState, action: SetConnected) {
return {
...state,
connecting: false,
connected: true,
tree: action.tree,
}
}
function setDisconnected(state: ConnectionState, action: SetDisconnected) {
return {
...state,
connecting: false,
connected: false,
connectionId: undefined,
tree: undefined,
}
}
function showError(state: ConnectionState, action: ShowError) {
return {
...state,
error: action.error,
}
}

View File

@@ -3,20 +3,18 @@ import * as q from '../../../backend/src/Model'
import { Action, Reducer, combineReducers } from 'redux'
import { trackEvent } from '../tracking'
import { MqttOptions } from '../../../backend/src/DataSource'
import { PublishState, publishReducer } from './Publish'
import { ConnectionState, connectionReducer } from './Connection'
export enum ActionTypes {
disconnect = 'DISCONNECT',
showError = 'SHOW_ERROR',
setAutoExpandLimit = 'SET_AUTO_EXPAND_LIMIT',
toggleSettingsVisibility = 'TOGGLE_SETTINGS_VISIBILITY',
setNodeOrder = 'SET_NODE_ORDER',
selectTopic = 'SELECT_TOPIC',
showUpdateNotification = 'SHOW_UPDATE_NOTIFICATION',
showUpdateDetails = 'SHOW_UPDATE_DETAILS',
connecting = 'CONNECTING',
connected = 'CONNECTED',
}
export interface CustomAction extends Action {
@@ -26,14 +24,12 @@ export interface CustomAction extends Action {
selectedTopic?: q.TreeNode
showUpdateNotification?: boolean
showUpdateDetails?: boolean
connectionOptions?: MqttOptions
connectionId?: string
error?: string
}
export interface AppState {
tooBigReducer: TooBigOfState
publish: PublishState
connection: ConnectionState
}
export interface TooBigOfState {
@@ -41,10 +37,6 @@ export interface TooBigOfState {
selectedTopic?: q.TreeNode
showUpdateNotification?: boolean
showUpdateDetails: boolean
connecting: boolean
connected: boolean
error?: string
connectionId?: string
}
export interface SettingsState {
@@ -68,9 +60,6 @@ const initialBigState: TooBigOfState = {
},
selectedTopic: undefined,
showUpdateDetails: false,
connected: false,
connecting: false,
error: undefined,
}
const tooBigReducer: Reducer<TooBigOfState | undefined, CustomAction> = (state = initialBigState, action) => {
@@ -134,38 +123,6 @@ const tooBigReducer: Reducer<TooBigOfState | undefined, CustomAction> = (state =
showUpdateDetails: action.showUpdateDetails,
}
case ActionTypes.connecting:
if (!action.connectionId) {
return state
}
return {
...state,
connected: false,
connecting: true,
connectionId: action.connectionId,
}
case ActionTypes.disconnect:
return {
...state,
connected: false,
connecting: false,
connectionId: undefined,
}
case ActionTypes.connected:
return {
...state,
connected: true,
connecting: false,
}
case ActionTypes.showError:
return {
...state,
error: action.error,
}
default:
return state
}
@@ -174,6 +131,7 @@ const tooBigReducer: Reducer<TooBigOfState | undefined, CustomAction> = (state =
const reducer = combineReducers({
tooBigReducer,
publish: publishReducer,
connection: connectionReducer,
})
export default reducer