diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index ef331fe1f5..d8ea45eac2 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -42,7 +42,8 @@ import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDelta import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import SettingsStore from "../../../settings/SettingsStore"; import CustomRoomTagStore from "../../../stores/CustomRoomTagStore"; -import { arrayHasDiff } from "../../../utils/arrays"; +import { arrayFastClone, arrayHasDiff } from "../../../utils/arrays"; +import { objectShallowClone } from "../../../utils/objects"; interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; @@ -255,7 +256,11 @@ export default class RoomList extends React.Component { } if (doUpdate) { - this.setState({sublists: newLists}, () => { + // We have to break our reference to the room list store if we want to be able to + // diff the object for changes, so do that. + const sublists = objectShallowClone(newLists, (k, v) => arrayFastClone(v)); + + this.setState({sublists: sublists}, () => { this.props.onResize(); }); } diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts index ca82059b01..2a5b1b5f16 100644 --- a/src/utils/arrays.ts +++ b/src/utils/arrays.ts @@ -14,6 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +/** + * Clones an array as fast as possible, retaining references of the array's values. + * @param a The array to clone. Must be defined. + * @returns A copy of the array. + */ +export function arrayFastClone(a: any[]): any[] { + return a.slice(0, a.length); +} + /** * Determines if the two arrays are different either in length, contents, * or order of those contents. diff --git a/src/utils/objects.ts b/src/utils/objects.ts index c3fbc64022..db8248759d 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -36,6 +36,28 @@ export function objectExcluding(a: any, props: string[]): any { }, {}); } +/** + * Clones an object to a caller-controlled depth. When a propertyCloner is supplied, the + * object's properties will be passed through it with the return value used as the new + * object's type. This is intended to be used to deep clone a reference, but without + * having to deep clone the entire object. This function is safe to call recursively within + * the propertyCloner. + * @param a The object to clone. Must be defined. + * @param propertyCloner The function to clone the properties of the object with, optionally. + * First argument is the property key with the second being the current value. + * @returns A cloned object. + */ +export function objectShallowClone(a: any, propertyCloner?: (k: string, v: any) => any): any { + const newObj = {}; + for (const [k, v] of Object.entries(a)) { + newObj[k] = v; + if (propertyCloner) { + newObj[k] = propertyCloner(k, v); + } + } + return newObj; +} + /** * Determines if the two objects, which are assumed to be of the same * key shape, have a difference in their values. If a difference is