Fix memory leaks
This commit is contained in:
@@ -78,6 +78,8 @@ export const disconnect = () => (dispatch: Dispatch<any>, getState: () => AppSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
tree && tree.stopUpdating()
|
tree && tree.stopUpdating()
|
||||||
|
tree && tree.destroy()
|
||||||
|
|
||||||
// Clear topic filter
|
// Clear topic filter
|
||||||
dispatch({
|
dispatch({
|
||||||
topicFilter: '',
|
topicFilter: '',
|
||||||
@@ -88,4 +90,5 @@ export const disconnect = () => (dispatch: Dispatch<any>, getState: () => AppSta
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: ActionTypes.CONNECTION_SET_DISCONNECTED,
|
type: ActionTypes.CONNECTION_SET_DISCONNECTED,
|
||||||
})
|
})
|
||||||
|
dispatch(showTree(undefined))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,20 +53,28 @@ const debouncedSelectTopic = debounce((topic: q.TreeNode<TopicViewModel>, dispat
|
|||||||
}
|
}
|
||||||
}, 70)
|
}, 70)
|
||||||
|
|
||||||
export const resetStore = () => (dispatch: Dispatch<any>): AnyAction => {
|
function destroyUnreferencedTree(state: AppState) {
|
||||||
|
const visibleTree = state.tree.get('tree')
|
||||||
|
const connectionTree = state.connection.tree
|
||||||
|
|
||||||
|
// Stop updates of old tree
|
||||||
|
if (visibleTree && visibleTree !== connectionTree) {
|
||||||
|
console.warn('destroy')
|
||||||
|
visibleTree.stopUpdating()
|
||||||
|
visibleTree.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const resetStore = () => (dispatch: Dispatch<any>, getState: () => AppState): AnyAction => {
|
||||||
|
destroyUnreferencedTree(getState())
|
||||||
|
|
||||||
return dispatch({
|
return dispatch({
|
||||||
type: ActionTypes.TREE_RESET_STORE,
|
type: ActionTypes.TREE_RESET_STORE,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showTree = (tree: q.Tree<TopicViewModel> | undefined) => (dispatch: Dispatch<any>, getState: () => AppState): AnyAction => {
|
export const showTree = (tree: q.Tree<TopicViewModel> | undefined) => (dispatch: Dispatch<any>, getState: () => AppState): AnyAction => {
|
||||||
const visibleTree = getState().tree.get('tree')
|
destroyUnreferencedTree(getState())
|
||||||
const connectionTree = getState().connection.tree
|
|
||||||
|
|
||||||
// Stop updates of old tree
|
|
||||||
if (visibleTree !== connectionTree && visibleTree) {
|
|
||||||
visibleTree.stopUpdating()
|
|
||||||
}
|
|
||||||
|
|
||||||
return dispatch({
|
return dispatch({
|
||||||
tree,
|
tree,
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
node: q.TreeNode<TopicViewModel>
|
|
||||||
compareMessage?: q.Message
|
compareMessage?: q.Message
|
||||||
valueRenderWidth: number
|
valueRenderWidth: number
|
||||||
}
|
}
|
||||||
@@ -49,8 +48,7 @@ class Sidebar extends React.Component<Props, State> {
|
|||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props)
|
super(props)
|
||||||
console.error('Find and fix me #state')
|
this.state = { valueRenderWidth: 300 }
|
||||||
this.state = { node: new q.Tree(), valueRenderWidth: 300 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerUpdateListener(node: q.TreeNode<TopicViewModel>) {
|
private registerUpdateListener(node: q.TreeNode<TopicViewModel>) {
|
||||||
@@ -156,7 +154,6 @@ class Sidebar extends React.Component<Props, State> {
|
|||||||
public componentWillReceiveProps(nextProps: Props) {
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
this.props.node && this.removeUpdateListener(this.props.node)
|
this.props.node && this.removeUpdateListener(this.props.node)
|
||||||
nextProps.node && this.registerUpdateListener(nextProps.node)
|
nextProps.node && this.registerUpdateListener(nextProps.node)
|
||||||
this.props.node && this.setState({ node: this.props.node })
|
|
||||||
|
|
||||||
if (this.props.node !== nextProps.node) {
|
if (this.props.node !== nextProps.node) {
|
||||||
this.setState({ compareMessage: undefined })
|
this.setState({ compareMessage: undefined })
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ interface State {
|
|||||||
lastUpdate: number
|
lastUpdate: number
|
||||||
}
|
}
|
||||||
|
|
||||||
class Tree extends React.PureComponent<Props, State> {
|
class TreeComponent extends React.PureComponent<Props, State> {
|
||||||
private updateTimer?: any
|
private updateTimer?: any
|
||||||
private perf: number = 0
|
private perf: number = 0
|
||||||
private renderTime = 0
|
private renderTime = 0
|
||||||
@@ -137,4 +137,4 @@ const mapDispatchToProps = (dispatch: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Tree)
|
export default connect(mapStateToProps, mapDispatchToProps)(TreeComponent)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ interface State {
|
|||||||
selected: boolean
|
selected: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class TreeNode extends React.Component<Props, State> {
|
class TreeNodeComponent extends React.Component<Props, State> {
|
||||||
private animationDirty: boolean = false
|
private animationDirty: boolean = false
|
||||||
|
|
||||||
private cssAnimationWasSetAt?: number
|
private cssAnimationWasSetAt?: number
|
||||||
@@ -97,8 +97,8 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
treeNode.viewModel.change.subscribe(this.viewStateHasChanged)
|
treeNode.viewModel.change.subscribe(this.viewStateHasChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewStateHasChanged = (msg: void, viewModel: TopicViewModel) => {
|
private viewStateHasChanged = (msg: void) => {
|
||||||
this.setState({ selected: viewModel.isSelected() })
|
this.setState({ selected: this.props.treeNode.viewModel!.isSelected() })
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeSubscriber(treeNode: q.TreeNode<TopicViewModel>) {
|
private removeSubscriber(treeNode: q.TreeNode<TopicViewModel>) {
|
||||||
@@ -243,4 +243,4 @@ class TreeNode extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles, { withTheme: true })(TreeNode)
|
export default withStyles(styles, { withTheme: true })(TreeNodeComponent)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { EventDispatcher } from '../../../events'
|
|||||||
|
|
||||||
export class TopicViewModel {
|
export class TopicViewModel {
|
||||||
private selected: boolean
|
private selected: boolean
|
||||||
public change = new EventDispatcher<void, TopicViewModel>(this)
|
public change = new EventDispatcher<void, TopicViewModel>()
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
this.selected = false
|
this.selected = false
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface DataSourceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DataSourceStateMachine {
|
export class DataSourceStateMachine {
|
||||||
public onUpdate = new EventDispatcher<DataSourceState, DataSourceStateMachine>(this)
|
public onUpdate = new EventDispatcher<DataSourceState, DataSourceStateMachine>()
|
||||||
private state: DataSourceState = {
|
private state: DataSourceState = {
|
||||||
error: undefined,
|
error: undefined,
|
||||||
connected: false,
|
connected: false,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class Tree<ViewModel> extends TreeNode<ViewModel> {
|
|||||||
public isTree = true
|
public isTree = true
|
||||||
private cachedHash = `${Math.random()}`
|
private cachedHash = `${Math.random()}`
|
||||||
private unmergedMessages: ChangeBuffer = new ChangeBuffer()
|
private unmergedMessages: ChangeBuffer = new ChangeBuffer()
|
||||||
public didReceive = new EventDispatcher<void, Tree<ViewModel>>(this)
|
public didReceive = new EventDispatcher<void, Tree<ViewModel>>()
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(undefined, undefined)
|
super(undefined, undefined)
|
||||||
@@ -27,6 +27,13 @@ export class Tree<ViewModel> extends TreeNode<ViewModel> {
|
|||||||
this.didReceive.dispatch()
|
this.didReceive.dispatch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
super.destroy()
|
||||||
|
this.updateSource && this.updateSource.unsubscribe(this.subscriptionEvent, this.handleNewData)
|
||||||
|
this.updateSource = undefined
|
||||||
|
this.didReceive.removeAllListeners()
|
||||||
|
}
|
||||||
|
|
||||||
public updateWithConnection(emitter: EventBusInterface, connectionId: string, nodeFilter?: (node: TreeNode<ViewModel>) => boolean) {
|
public updateWithConnection(emitter: EventBusInterface, connectionId: string, nodeFilter?: (node: TreeNode<ViewModel>) => boolean) {
|
||||||
this.updateSource = emitter
|
this.updateSource = emitter
|
||||||
this.connectionId = connectionId
|
this.connectionId = connectionId
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export class TreeNode<ViewModel> {
|
|||||||
public collapsed = false
|
public collapsed = false
|
||||||
public messages: number = 0
|
public messages: number = 0
|
||||||
public lastUpdate: number = Date.now()
|
public lastUpdate: number = Date.now()
|
||||||
public onMerge = new EventDispatcher<void, TreeNode<ViewModel>>(this)
|
public onMerge = new EventDispatcher<void, TreeNode<ViewModel>>()
|
||||||
public onEdgesChange = new EventDispatcher<void, TreeNode<ViewModel>>(this)
|
public onEdgesChange = new EventDispatcher<void, TreeNode<ViewModel>>()
|
||||||
public onMessage = new EventDispatcher<Message, TreeNode<ViewModel>>(this)
|
public onMessage = new EventDispatcher<Message, TreeNode<ViewModel>>()
|
||||||
public isTree = false
|
public isTree = false
|
||||||
|
|
||||||
private cachedPath?: string
|
private cachedPath?: string
|
||||||
@@ -99,6 +99,21 @@ export class TreeNode<ViewModel> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
for (const edge of this.edgeArray) {
|
||||||
|
edge.target.destroy()
|
||||||
|
}
|
||||||
|
this.edgeArray = []
|
||||||
|
this.edges = {}
|
||||||
|
this.cachedChildTopics = []
|
||||||
|
this.sourceEdge = undefined
|
||||||
|
this.onMerge.removeAllListeners()
|
||||||
|
this.onEdgesChange.removeAllListeners()
|
||||||
|
this.onMessage.removeAllListeners()
|
||||||
|
this.messageHistory = new RingBuffer<Message>(1, 1)
|
||||||
|
this.message = undefined
|
||||||
|
}
|
||||||
|
|
||||||
public unconnectedClone() {
|
public unconnectedClone() {
|
||||||
const node = new TreeNode<ViewModel>()
|
const node = new TreeNode<ViewModel>()
|
||||||
node.message = this.message
|
node.message = this.message
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export class ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UpdateNotifier {
|
class UpdateNotifier {
|
||||||
public onCheckUpdateRequest = new EventDispatcher<void, UpdateNotifier>(this)
|
public onCheckUpdateRequest = new EventDispatcher<void, UpdateNotifier>()
|
||||||
constructor() {
|
constructor() {
|
||||||
backendEvents.subscribe(checkForUpdates, () => {
|
backendEvents.subscribe(checkForUpdates, () => {
|
||||||
this.onCheckUpdateRequest.dispatch()
|
this.onCheckUpdateRequest.dispatch()
|
||||||
|
|||||||
@@ -7,20 +7,15 @@ interface CallbackStore {
|
|||||||
|
|
||||||
export class EventDispatcher<Message, Dispatcher> {
|
export class EventDispatcher<Message, Dispatcher> {
|
||||||
private emitter = new EventEmitter()
|
private emitter = new EventEmitter()
|
||||||
private dispatcher: Dispatcher
|
|
||||||
private callbacks: Array<CallbackStore> = []
|
private callbacks: Array<CallbackStore> = []
|
||||||
|
|
||||||
constructor(dispatcher: Dispatcher) {
|
|
||||||
this.dispatcher = dispatcher
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispatch(msg: Message) {
|
public dispatch(msg: Message) {
|
||||||
this.emitter.emit('event', msg)
|
this.emitter.emit('event', msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribe(callback: (msg: Message, dispatcher: Dispatcher) => void) {
|
public subscribe(callback: (msg: Message) => void) {
|
||||||
const wrappedCallback = (msg: Message) => {
|
const wrappedCallback = (msg: Message) => {
|
||||||
callback(msg, this.dispatcher)
|
callback(msg)
|
||||||
}
|
}
|
||||||
this.emitter.on('event', wrappedCallback)
|
this.emitter.on('event', wrappedCallback)
|
||||||
|
|
||||||
@@ -30,7 +25,7 @@ export class EventDispatcher<Message, Dispatcher> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsubscribe(callback: (msg: Message, dispatcher: Dispatcher) => void) {
|
public unsubscribe(callback: (msg: Message) => void) {
|
||||||
const item = this.callbacks.find(store => store.callback === callback)
|
const item = this.callbacks.find(store => store.callback === callback)
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user