diff --git a/client/coral-framework/components/Slot.js b/client/coral-framework/components/Slot.js index b1df6647a..f86d44eb1 100644 --- a/client/coral-framework/components/Slot.js +++ b/client/coral-framework/components/Slot.js @@ -4,19 +4,48 @@ import styles from './Slot.css'; import {connect} from 'react-redux'; import {getSlotElements} from 'coral-framework/helpers/plugins'; import omit from 'lodash/omit'; +import union from 'lodash/union'; +import isEqual from 'lodash/isEqual'; -function Slot ({fill, inline = false, className, reduxState, defaultComponent: DefaultComponent, ...rest}) { - let children = getSlotElements(fill, reduxState, rest); - const pluginConfig = reduxState.config.pluginConfig || {}; - if (children.length === 0 && DefaultComponent) { - children = ; +class Slot extends React.Component { + shouldComponentUpdate(next) { + + // Prevent Slot from rerendering when only reduxState has changed and + // it does not result in a change of slot children. + const keys = union(Object.keys(this.props), Object.keys(next)); + const changes = keys.filter((key) => this.props[key] !== next[key]); + if (changes.length === 1 && changes[0] === 'reduxState') { + const prevChildrenUuid = this.getChildren(this.props).map((child) => child.type.talkUuid); + const nextChildrenUuid = this.getChildren(next).map((child) => child.type.talkUuid); + return !isEqual(prevChildrenUuid, nextChildrenUuid); + } + + // Prevent Slot from rerendering when no props has shallowly changed. + return changes.length !== 0; } - return ( -
- {children} -
- ); + getSlotProps({fill: _a, inline: _b, className: _c, reduxState: _d, defaultComponent_: _e, ...rest} = this.props) { + return rest; + } + + getChildren(props = this.props) { + return getSlotElements(props.fill, props.reduxState, this.getSlotProps(props)); + } + + render() { + const {inline = false, className, reduxState, defaultComponent: DefaultComponent} = this.props; + let children = this.getChildren(); + const pluginConfig = reduxState.config.pluginConfig || {}; + if (children.length === 0 && DefaultComponent) { + children = ; + } + + return ( +
+ {children} +
+ ); + } } Slot.propTypes = { diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index b2ca78142..7bebaa1f6 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -9,6 +9,7 @@ import {loadTranslations} from 'coral-framework/services/i18n'; import {injectReducers} from 'coral-framework/services/store'; import camelize from './camelize'; import plugins from 'pluginsConfig'; +import uuid from 'uuid/v4'; export function getSlotComponents(slot, reduxState, props = {}) { const pluginConfig = reduxState.config.plugin_config || {}; @@ -100,7 +101,12 @@ function addMetaDataToSlotComponents() { const slots = plugin.module.slots; slots && Object.keys(slots).forEach((slot) => { slots[slot].forEach((component) => { + + // Attach plugin name to the component component.talkPluginName = plugin.name; + + // Attach uuid to the component + component.talkUuid = uuid(); }); }); });