Skip to content

Commit 732f7be

Browse files
committed
perf(UISrefActive): refactor classes update to improve performance
Remove `forceUpdate` calls and set active classes via `setState` to prevent unnecessary rerenders
1 parent 088bb02 commit 732f7be

File tree

1 file changed

+29
-15
lines changed

1 file changed

+29
-15
lines changed

src/components/UISrefActive.tsx

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
* @module components
44
*/ /** */
55
import * as React from 'react';
6-
import {Component, cloneElement, ValidationMap} from 'react';
6+
import { Component, cloneElement, ValidationMap } from 'react';
77
import * as PropTypes from 'prop-types';
88
import * as _classNames from 'classnames';
99

10-
import {UIRouterReact, UISref} from '../index';
11-
import {UIViewAddress} from './UIView';
10+
import { UIRouterReact, UISref } from '../index';
11+
import { UIViewAddress } from './UIView';
1212

1313
let classNames = _classNames;
1414

@@ -19,15 +19,15 @@ export interface UISrefActiveProps {
1919
}
2020

2121
export interface UISrefActiveState {
22-
state: {name: string; [key: string]: any};
22+
state: { name: string; [key: string]: any };
2323
params: Object;
2424
hash: string;
2525
}
2626

2727
export class UISrefActive extends Component<UISrefActiveProps, any> {
2828
// keep track of states to watch and their activeClasses
2929
states: Array<UISrefActiveState> = [];
30-
activeClasses: {[key: string]: string} = {};
30+
activeClasses: { [key: string]: string } = {};
3131

3232
// deregister the callback for state changed when unmounted
3333
deregister: Function;
@@ -46,6 +46,10 @@ export class UISrefActive extends Component<UISrefActiveProps, any> {
4646
parentUiSrefActiveAddStateInfo: PropTypes.func,
4747
};
4848

49+
state = {
50+
activeClasses: '',
51+
};
52+
4953
getChildContext() {
5054
return {
5155
parentUiSrefActiveAddStateInfo: this.addStateInfo,
@@ -62,7 +66,7 @@ export class UISrefActive extends Component<UISrefActiveProps, any> {
6266
// register callback for state change
6367
this.deregister = this.context[
6468
'router'
65-
].transitionService.onSuccess({}, () => this.forceUpdate());
69+
].transitionService.onSuccess({}, () => this.updateActiveClasses());
6670
}
6771

6872
componentWillUnmount() {
@@ -72,19 +76,19 @@ export class UISrefActive extends Component<UISrefActiveProps, any> {
7276
addStateInfo = (stateName, stateParams) => {
7377
const activeClass = this.props.class;
7478
let deregister = this.addState(stateName, stateParams, activeClass);
75-
this.forceUpdate();
79+
this.updateActiveClasses();
7680
return deregister;
7781
};
7882

7983
addState = (stateName, stateParams, activeClass) => {
80-
const {stateService} = this.context['router'];
84+
const { stateService } = this.context['router'];
8185
let parent = this.context['parentUIViewAddress'];
8286
let stateContext =
8387
(parent && parent.context) || this.context['router'].stateRegistry.root();
8488
let state = stateService.get(stateName, stateContext);
8589
let stateHash = this.createStateHash(stateName, stateParams);
8690
let stateInfo = {
87-
state: state || {name: stateName},
91+
state: state || { name: stateName },
8892
params: stateParams,
8993
hash: stateHash,
9094
};
@@ -105,22 +109,32 @@ export class UISrefActive extends Component<UISrefActiveProps, any> {
105109
: state;
106110
};
107111

108-
getActiveClasses = () => {
112+
getActiveClasses = (): string => {
109113
let activeClasses = [];
110-
let {stateService} = this.context['router'];
111-
let {exact} = this.props;
114+
let { stateService } = this.context['router'];
115+
let { exact } = this.props;
112116
this.states.forEach(s => {
113-
let {state, params, hash} = s;
117+
let { state, params, hash } = s;
114118
if (!exact && stateService.includes(state.name, params))
115119
activeClasses.push(this.activeClasses[hash]);
116120
if (exact && stateService.is(state.name, params))
117121
activeClasses.push(this.activeClasses[hash]);
118122
});
119-
return activeClasses;
123+
return classNames(activeClasses);
124+
};
125+
126+
updateActiveClasses = (): void => {
127+
const { activeClasses } = this.state;
128+
const newActiveClasses = this.getActiveClasses();
129+
if (activeClasses !== newActiveClasses) {
130+
this.setState({
131+
activeClasses: this.getActiveClasses(),
132+
});
133+
}
120134
};
121135

122136
render() {
123-
let activeClasses = this.getActiveClasses();
137+
const { activeClasses } = this.state;
124138
return activeClasses.length > 0
125139
? cloneElement(
126140
this.props.children,

0 commit comments

Comments
 (0)