prevent unnecessary rerenders
Summary: Updates of the redux store caused rerendering of UI components, even without the acutal UI changing. This diff ensures the UI is only updated, when The PluginContainer received all plugin states and selected the pluginState of the active plugin to pass it down to the plugin. However, this caused a rerender everytime a pluginState changed (even if the plugin was in the background). This diff moves the selection of the active plugin to the `connect`-function and only passes the state of the active plugin into the container. This makes sure the plugin rerenders only if it's own state changes. The main sidebar displays the number of notifications. Therefore, it was passed the array of notifications. However, this array is regenerated, everytime a new notification **might** be triggered. Now, the number of notifications is calculated in the `connect`-method and only the number itself is passed into the component. This makes sure the sidebar is only rerendered, when the actual number of notifications changes. Reviewed By: passy Differential Revision: D13276096 fbshipit-source-id: bf1e6c4a186f7a1cf7f7427cd3523b5b71eb003a
This commit is contained in:
committed by
Facebook Github Bot
parent
34e75c3c5f
commit
133788380e
@@ -12,7 +12,7 @@ import type {Props as PluginProps} from './plugin';
|
|||||||
import Client from './Client.js';
|
import Client from './Client.js';
|
||||||
import {
|
import {
|
||||||
ErrorBoundary,
|
ErrorBoundary,
|
||||||
Component,
|
PureComponent,
|
||||||
FlexColumn,
|
FlexColumn,
|
||||||
FlexRow,
|
FlexRow,
|
||||||
colors,
|
colors,
|
||||||
@@ -40,71 +40,24 @@ const SidebarContainer = styled(FlexRow)({
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
logger: LogManager,
|
logger: LogManager,
|
||||||
selectedDevice: BaseDevice,
|
pluginState: Object,
|
||||||
selectedPlugin: ?string,
|
activePlugin: ?Class<FlipperPlugin<> | FlipperDevicePlugin<>>,
|
||||||
selectedApp: ?string,
|
target: Client | BaseDevice | null,
|
||||||
pluginStates: {
|
pluginKey: string,
|
||||||
[pluginKey: string]: Object,
|
|
||||||
},
|
|
||||||
clients: Array<Client>,
|
|
||||||
setPluginState: (payload: {
|
|
||||||
pluginKey: string,
|
|
||||||
state: Object,
|
|
||||||
}) => void,
|
|
||||||
deepLinkPayload: ?string,
|
deepLinkPayload: ?string,
|
||||||
|
|
||||||
selectPlugin: (payload: {|
|
selectPlugin: (payload: {|
|
||||||
selectedPlugin: ?string,
|
selectedPlugin: ?string,
|
||||||
selectedApp?: ?string,
|
selectedApp?: ?string,
|
||||||
deepLinkPayload: ?string,
|
deepLinkPayload: ?string,
|
||||||
|}) => mixed,
|
|}) => mixed,
|
||||||
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
|
setPluginState: (payload: {
|
||||||
clientPlugins: Map<string, Class<FlipperPlugin<>>>,
|
pluginKey: string,
|
||||||
|
state: Object,
|
||||||
|
}) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
class PluginContainer extends PureComponent<Props> {
|
||||||
activePlugin: ?Class<FlipperPlugin<> | FlipperDevicePlugin<>>,
|
|
||||||
target: Client | BaseDevice | null,
|
|
||||||
pluginKey: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
class PluginContainer extends Component<Props, State> {
|
|
||||||
static getDerivedStateFromProps(props: Props): State {
|
|
||||||
const {selectedPlugin} = props;
|
|
||||||
let pluginKey = 'unknown';
|
|
||||||
let target = null;
|
|
||||||
let activePlugin: ?Class<FlipperPlugin<> | FlipperDevicePlugin<>> = null;
|
|
||||||
|
|
||||||
if (selectedPlugin) {
|
|
||||||
if (selectedPlugin === NotificationsHub.id) {
|
|
||||||
activePlugin = NotificationsHub;
|
|
||||||
} else if (props.selectedPlugin) {
|
|
||||||
activePlugin = props.devicePlugins.get(props.selectedPlugin);
|
|
||||||
}
|
|
||||||
target = props.selectedDevice;
|
|
||||||
if (activePlugin) {
|
|
||||||
pluginKey = `${props.selectedDevice.serial}#${activePlugin.id}`;
|
|
||||||
} else {
|
|
||||||
target = props.clients.find(
|
|
||||||
(client: Client) => client.id === props.selectedApp,
|
|
||||||
);
|
|
||||||
activePlugin = props.clientPlugins.get(selectedPlugin);
|
|
||||||
if (!activePlugin || !target) {
|
|
||||||
throw new Error(
|
|
||||||
`Plugin "${props.selectedPlugin || ''}" could not be found.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pluginKey = `${target.id}#${activePlugin.id}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
activePlugin,
|
|
||||||
target,
|
|
||||||
pluginKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
state: State = this.constructor.getDerivedStateFromProps(this.props);
|
|
||||||
plugin: ?FlipperPlugin<> | FlipperDevicePlugin<>;
|
plugin: ?FlipperPlugin<> | FlipperDevicePlugin<>;
|
||||||
|
|
||||||
refChanged = (ref: ?FlipperPlugin<> | FlipperDevicePlugin<>) => {
|
refChanged = (ref: ?FlipperPlugin<> | FlipperDevicePlugin<>) => {
|
||||||
@@ -112,8 +65,7 @@ class PluginContainer extends Component<Props, State> {
|
|||||||
this.plugin._teardown();
|
this.plugin._teardown();
|
||||||
this.plugin = null;
|
this.plugin = null;
|
||||||
}
|
}
|
||||||
const {target} = this.state;
|
if (ref && this.props.target) {
|
||||||
if (ref && target) {
|
|
||||||
activateMenuItems(ref);
|
activateMenuItems(ref);
|
||||||
ref._init();
|
ref._init();
|
||||||
this.props.logger.trackTimeSince(`activePlugin-${ref.constructor.id}`);
|
this.props.logger.trackTimeSince(`activePlugin-${ref.constructor.id}`);
|
||||||
@@ -122,8 +74,13 @@ class PluginContainer extends Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {pluginStates, setPluginState} = this.props;
|
const {
|
||||||
const {activePlugin, pluginKey, target} = this.state;
|
pluginState,
|
||||||
|
setPluginState,
|
||||||
|
activePlugin,
|
||||||
|
pluginKey,
|
||||||
|
target,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (!activePlugin || !target) {
|
if (!activePlugin || !target) {
|
||||||
return null;
|
return null;
|
||||||
@@ -134,14 +91,14 @@ class PluginContainer extends Component<Props, State> {
|
|||||||
persistedState: activePlugin.defaultPersistedState
|
persistedState: activePlugin.defaultPersistedState
|
||||||
? {
|
? {
|
||||||
...activePlugin.defaultPersistedState,
|
...activePlugin.defaultPersistedState,
|
||||||
...pluginStates[pluginKey],
|
...pluginState,
|
||||||
}
|
}
|
||||||
: pluginStates[pluginKey],
|
: pluginState,
|
||||||
setPersistedState: state => setPluginState({pluginKey, state}),
|
setPersistedState: state => setPluginState({pluginKey, state}),
|
||||||
target,
|
target,
|
||||||
deepLinkPayload: this.props.deepLinkPayload,
|
deepLinkPayload: this.props.deepLinkPayload,
|
||||||
selectPlugin: (pluginID: string, deepLinkPayload: ?string) => {
|
selectPlugin: (pluginID: string, deepLinkPayload: ?string) => {
|
||||||
const {target} = this.state;
|
const {target} = this.props;
|
||||||
// check if plugin will be available
|
// check if plugin will be available
|
||||||
if (
|
if (
|
||||||
target instanceof Client &&
|
target instanceof Client &&
|
||||||
@@ -192,16 +149,40 @@ export default connect(
|
|||||||
},
|
},
|
||||||
pluginStates,
|
pluginStates,
|
||||||
plugins: {devicePlugins, clientPlugins},
|
plugins: {devicePlugins, clientPlugins},
|
||||||
}) => ({
|
}) => {
|
||||||
selectedPlugin,
|
let pluginKey = 'unknown';
|
||||||
selectedDevice,
|
let target = null;
|
||||||
pluginStates,
|
let activePlugin: ?Class<FlipperPlugin<> | FlipperDevicePlugin<>> = null;
|
||||||
selectedApp,
|
|
||||||
clients,
|
if (selectedPlugin) {
|
||||||
deepLinkPayload,
|
if (selectedPlugin === NotificationsHub.id) {
|
||||||
devicePlugins,
|
activePlugin = NotificationsHub;
|
||||||
clientPlugins,
|
} else if (selectedPlugin) {
|
||||||
}),
|
activePlugin = devicePlugins.get(selectedPlugin);
|
||||||
|
}
|
||||||
|
target = selectedDevice;
|
||||||
|
if (activePlugin) {
|
||||||
|
pluginKey = `${selectedDevice.serial}#${activePlugin.id}`;
|
||||||
|
} else {
|
||||||
|
target = clients.find((client: Client) => client.id === selectedApp);
|
||||||
|
activePlugin = clientPlugins.get(selectedPlugin);
|
||||||
|
if (!activePlugin || !target) {
|
||||||
|
throw new Error(
|
||||||
|
`Plugin "${selectedPlugin || ''}" could not be found.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pluginKey = `${target.id}#${activePlugin.id}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
pluginState: pluginStates[pluginKey],
|
||||||
|
activePlugin,
|
||||||
|
target,
|
||||||
|
deepLinkPayload,
|
||||||
|
pluginKey,
|
||||||
|
};
|
||||||
|
},
|
||||||
{
|
{
|
||||||
setPluginState,
|
setPluginState,
|
||||||
selectPlugin,
|
selectPlugin,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type {UninitializedClient} from '../UninitializedClient.js';
|
|||||||
import type {PluginNotification} from '../reducers/notifications';
|
import type {PluginNotification} from '../reducers/notifications';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
PureComponent,
|
||||||
Component,
|
Component,
|
||||||
Sidebar,
|
Sidebar,
|
||||||
FlexBox,
|
FlexBox,
|
||||||
@@ -177,13 +178,12 @@ type MainSidebarProps = {|
|
|||||||
client: UninitializedClient,
|
client: UninitializedClient,
|
||||||
deviceId?: string,
|
deviceId?: string,
|
||||||
}>,
|
}>,
|
||||||
activeNotifications: Array<PluginNotification>,
|
numNotifications: number,
|
||||||
blacklistedPlugins: Array<string>,
|
|
||||||
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
|
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
|
||||||
clientPlugins: Map<string, Class<FlipperPlugin<>>>,
|
clientPlugins: Map<string, Class<FlipperPlugin<>>>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
class MainSidebar extends Component<MainSidebarProps> {
|
class MainSidebar extends PureComponent<MainSidebarProps> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
@@ -191,7 +191,7 @@ class MainSidebar extends Component<MainSidebarProps> {
|
|||||||
selectedApp,
|
selectedApp,
|
||||||
selectPlugin,
|
selectPlugin,
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
activeNotifications,
|
numNotifications,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let {clients, uninitializedClients} = this.props;
|
let {clients, uninitializedClients} = this.props;
|
||||||
|
|
||||||
@@ -202,11 +202,6 @@ class MainSidebar extends Component<MainSidebarProps> {
|
|||||||
)
|
)
|
||||||
.sort((a, b) => (a.query.app || '').localeCompare(b.query.app));
|
.sort((a, b) => (a.query.app || '').localeCompare(b.query.app));
|
||||||
|
|
||||||
let blacklistedPlugins = new Set(this.props.blacklistedPlugins);
|
|
||||||
const notifications = activeNotifications.filter(
|
|
||||||
(n: PluginNotification) => !blacklistedPlugins.has(n.pluginId),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
position="left"
|
position="left"
|
||||||
@@ -226,13 +221,11 @@ class MainSidebar extends Component<MainSidebarProps> {
|
|||||||
}>
|
}>
|
||||||
<PluginIcon
|
<PluginIcon
|
||||||
color={colors.light50}
|
color={colors.light50}
|
||||||
name={
|
name={numNotifications > 0 ? NotificationsHub.icon : 'bell-null'}
|
||||||
notifications.length > 0 ? NotificationsHub.icon : 'bell-null'
|
|
||||||
}
|
|
||||||
isActive={selectedPlugin === NotificationsHub.id}
|
isActive={selectedPlugin === NotificationsHub.id}
|
||||||
/>
|
/>
|
||||||
<PluginName
|
<PluginName
|
||||||
count={notifications.length}
|
count={numNotifications}
|
||||||
isActive={selectedPlugin === NotificationsHub.id}>
|
isActive={selectedPlugin === NotificationsHub.id}>
|
||||||
{NotificationsHub.title}
|
{NotificationsHub.title}
|
||||||
</PluginName>
|
</PluginName>
|
||||||
@@ -322,8 +315,12 @@ export default connect(
|
|||||||
notifications: {activeNotifications, blacklistedPlugins},
|
notifications: {activeNotifications, blacklistedPlugins},
|
||||||
plugins: {devicePlugins, clientPlugins},
|
plugins: {devicePlugins, clientPlugins},
|
||||||
}) => ({
|
}) => ({
|
||||||
blacklistedPlugins,
|
numNotifications: (() => {
|
||||||
activeNotifications,
|
const blacklist = new Set(blacklistedPlugins);
|
||||||
|
return activeNotifications.filter(
|
||||||
|
(n: PluginNotification) => !blacklist.has(n.pluginId),
|
||||||
|
).length;
|
||||||
|
})(),
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
selectedPlugin,
|
selectedPlugin,
|
||||||
|
|||||||
Reference in New Issue
Block a user