diff --git a/src/components/common/messaging/Message.tsx b/src/components/common/messaging/Message.tsx
index a7a92d369d6e6f2507dd8bbf84052632d0336edb..54099c66fafa3de15a9c1632fe54ba97b508fcc7 100644
--- a/src/components/common/messaging/Message.tsx
+++ b/src/components/common/messaging/Message.tsx
@@ -9,7 +9,6 @@ import { QueuedMessage } from "../../../redux/reducers/queue";
 
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
-import { useUser } from "../../../context/revoltjs/hooks";
 import { MessageObject } from "../../../context/revoltjs/util";
 
 import Overline from "../../ui/Overline";
diff --git a/src/components/common/messaging/SystemMessage.tsx b/src/components/common/messaging/SystemMessage.tsx
index 59051baf59e20c5be381d0e7921413f07d210298..19c5325c525631bb52da3859e1bd3f586a2d0c04 100644
--- a/src/components/common/messaging/SystemMessage.tsx
+++ b/src/components/common/messaging/SystemMessage.tsx
@@ -8,7 +8,6 @@ import { TextReact } from "../../../lib/i18n";
 import { User } from "../../../mobx";
 import { useData } from "../../../mobx/State";
 
-import { useForceUpdate, useUser } from "../../../context/revoltjs/hooks";
 import { MessageObject } from "../../../context/revoltjs/util";
 
 import UserShort from "../user/UserShort";
diff --git a/src/components/common/messaging/attachments/MessageReply.tsx b/src/components/common/messaging/attachments/MessageReply.tsx
index b849e3db3f7618b060c4d10b02e007cc0d611a51..dcf3517859f593dbd62b3d3bb1fbc38b076fdfd9 100644
--- a/src/components/common/messaging/attachments/MessageReply.tsx
+++ b/src/components/common/messaging/attachments/MessageReply.tsx
@@ -13,7 +13,7 @@ import { useRenderState } from "../../../../lib/renderer/Singleton";
 
 import { useData } from "../../../../mobx/State";
 
-import { useForceUpdate, useUser } from "../../../../context/revoltjs/hooks";
+import { useClient } from "../../../../context/revoltjs/RevoltClient";
 import { mapMessage, MessageObject } from "../../../../context/revoltjs/util";
 
 import Markdown from "../../../markdown/Markdown";
@@ -124,7 +124,7 @@ export const ReplyBase = styled.div<{
 `;
 
 export const MessageReply = observer(({ index, channel, id }: Props) => {
-    const ctx = useForceUpdate();
+    const client = useClient();
     const view = useRenderState(channel);
     if (view?.type !== "RENDER") return null;
 
@@ -138,7 +138,7 @@ export const MessageReply = observer(({ index, channel, id }: Props) => {
         if (m) {
             setMessage(m);
         } else {
-            ctx.client.channels
+            client.channels
                 .fetchMessage(channel, id)
                 .then((m) => setMessage(mapMessage(m)));
         }
@@ -178,8 +178,7 @@ export const MessageReply = observer(({ index, channel, id }: Props) => {
                             <div
                                 className="content"
                                 onClick={() => {
-                                    const obj =
-                                        ctx.client.channels.get(channel);
+                                    const obj = client.channels.get(channel);
                                     if (obj?.channel_type === "TextChannel") {
                                         history.push(
                                             `/server/${obj.server}/channel/${obj._id}/${message._id}`,
diff --git a/src/components/common/messaging/bars/ReplyBar.tsx b/src/components/common/messaging/bars/ReplyBar.tsx
index 5de69066daf9d520bf15ff2c57b9da50f3bc46ab..a6b282d091faac43c358236b25b2229d480ec51c 100644
--- a/src/components/common/messaging/bars/ReplyBar.tsx
+++ b/src/components/common/messaging/bars/ReplyBar.tsx
@@ -17,8 +17,6 @@ import { useRenderState } from "../../../../lib/renderer/Singleton";
 import { useData } from "../../../../mobx/State";
 import { Reply } from "../../../../redux/reducers/queue";
 
-import { useUsers } from "../../../../context/revoltjs/hooks";
-
 import IconButton from "../../../ui/IconButton";
 
 import Markdown from "../../../markdown/Markdown";
diff --git a/src/components/common/messaging/bars/TypingIndicator.tsx b/src/components/common/messaging/bars/TypingIndicator.tsx
index 8c2fe0be25c81461464d092405ad8f15b1c33a60..816b7fc5533dc0135b96a5a81764c959ac2ccc18 100644
--- a/src/components/common/messaging/bars/TypingIndicator.tsx
+++ b/src/components/common/messaging/bars/TypingIndicator.tsx
@@ -15,7 +15,6 @@ import {
     AppContext,
     useClient,
 } from "../../../../context/revoltjs/RevoltClient";
-import { useUsers } from "../../../../context/revoltjs/hooks";
 
 import { Username } from "../../user/UserShort";
 
diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx
index b1203138ce94eb013a0fb0bb134a8eb1329ca50a..e22d35bffb6a6db3f1f3cbe18c97afd9c4e9ba78 100644
--- a/src/components/navigation/left/HomeSidebar.tsx
+++ b/src/components/navigation/left/HomeSidebar.tsx
@@ -23,11 +23,7 @@ import { Unreads } from "../../../redux/reducers/unreads";
 
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
-import {
-    useDMs,
-    useForceUpdate,
-    useUsers,
-} from "../../../context/revoltjs/hooks";
+import { useDMs, useForceUpdate } from "../../../context/revoltjs/hooks";
 
 import Category from "../../ui/Category";
 import placeholderSVG from "../items/placeholder.svg";
diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx
index 8beac5b89014c0fced4dfd127c36abaab1f1c753..23d65554c8282a45158c87716666faca8481f5ce 100644
--- a/src/components/navigation/right/MemberSidebar.tsx
+++ b/src/components/navigation/right/MemberSidebar.tsx
@@ -21,7 +21,6 @@ import {
     HookContext,
     useChannel,
     useForceUpdate,
-    useUsers,
 } from "../../../context/revoltjs/hooks";
 
 import CollapsibleSection from "../../common/CollapsibleSection";
diff --git a/src/context/intermediate/popovers/UserProfile.tsx b/src/context/intermediate/popovers/UserProfile.tsx
index c321b9f4c01d0cc40f0ee6f6d2bbec29a0f389be..0e0105d92e39a2fdc850c2ff3705fba0fadc68b9 100644
--- a/src/context/intermediate/popovers/UserProfile.tsx
+++ b/src/context/intermediate/popovers/UserProfile.tsx
@@ -31,7 +31,6 @@ import {
     useChannels,
     useForceUpdate,
     useUserPermission,
-    useUsers,
 } from "../../revoltjs/hooks";
 import { useIntermediate } from "../Intermediate";
 
diff --git a/src/context/revoltjs/hooks.ts b/src/context/revoltjs/hooks.ts
index e693ccf78f2a39c9933346610a9f51984e7f9c0a..e5ea6876c28c98943452fd6f8496f1200017c2a5 100644
--- a/src/context/revoltjs/hooks.ts
+++ b/src/context/revoltjs/hooks.ts
@@ -77,18 +77,6 @@ function useObject(
         : map.toArray();
 }
 
-export function useUser(id?: string, context?: HookContext) {
-    if (typeof id === "undefined") return;
-    return useObject("users", id, context) as Readonly<Users.User> | undefined;
-}
-
-export function useUsers(ids?: string[], context?: HookContext) {
-    return useObject("users", ids, context) as (
-        | Readonly<Users.User>
-        | undefined
-    )[];
-}
-
 export function useChannel(id?: string, context?: HookContext) {
     if (typeof id === "undefined") return;
     return useObject("channels", id, context) as
diff --git a/src/mobx/index.ts b/src/mobx/index.ts
index d878cd1fd36fd0ec5cd1f70c1c275811bbe67b75..fdc1ae4ca8998a76baaab03cb8de615cf88ad255 100644
--- a/src/mobx/index.ts
+++ b/src/mobx/index.ts
@@ -9,8 +9,8 @@ import {
     action,
     extendObservable,
 } from "mobx";
-import { Attachment, Users } from "revolt.js/dist/api/objects";
-import { RemoveUserField } from "revolt.js/dist/api/routes";
+import { Attachment, Channels, Users } from "revolt.js/dist/api/objects";
+import { RemoveChannelField, RemoveUserField } from "revolt.js/dist/api/routes";
 import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
 
 type Nullable<T> = T | null;
@@ -42,7 +42,7 @@ export class User {
     }
 
     @action update(data: Partial<Users.User>, clear?: RemoveUserField) {
-        const apply = (key: keyof Users.User) => {
+        const apply = (key: string) => {
             // This code has been tested.
             // @ts-expect-error
             if (data[key] && !isEqual(this[key], data[key])) {
@@ -62,6 +62,7 @@ export class User {
             }
         }
 
+        apply("username");
         apply("avatar");
         apply("badges");
         apply("status");
@@ -70,8 +71,110 @@ export class User {
     }
 }
 
+export class Channel {
+    _id: string;
+    type: Channels.Channel["channel_type"];
+
+    // Direct Message
+    active: Nullable<boolean> = null;
+
+    // Group
+    owner: Nullable<string> = null;
+
+    // Server
+    server: Nullable<string> = null;
+
+    // Permissions
+    permissions: Nullable<number> = null;
+    default_permissions: Nullable<number> = null;
+    role_permissions: Nullable<{ [key: string]: number }> = null;
+
+    // Common
+    name: Nullable<string> = null;
+    icon: Nullable<Attachment> = null;
+    description: Nullable<string> = null;
+    recipients: Nullable<string[]> = null;
+    last_message: Nullable<string | Channels.LastMessage> = null;
+
+    constructor(data: Channels.Channel) {
+        this._id = data._id;
+        this.type = data.channel_type;
+
+        switch (data.channel_type) {
+            case "DirectMessage": {
+                this.active = toNullable(data.active);
+                this.recipients = toNullable(data.recipients);
+                this.last_message = toNullable(data.last_message);
+                break;
+            }
+            case "Group": {
+                this.recipients = toNullable(data.recipients);
+                this.name = toNullable(data.name);
+                this.owner = toNullable(data.owner);
+                this.description = toNullable(data.description);
+                this.last_message = toNullable(data.last_message);
+                this.icon = toNullable(data.icon);
+                this.permissions = toNullable(data.permissions);
+                break;
+            }
+            case "TextChannel":
+            case "VoiceChannel": {
+                this.server = toNullable(data.server);
+                this.name = toNullable(data.name);
+                this.description = toNullable(data.description);
+                this.icon = toNullable(data.icon);
+                this.default_permissions = toNullable(data.default_permissions);
+                this.role_permissions = toNullable(data.role_permissions);
+
+                if (data.channel_type === "TextChannel") {
+                    this.last_message = toNullable(data.last_message);
+                }
+
+                break;
+            }
+        }
+
+        makeAutoObservable(this);
+    }
+
+    @action update(
+        data: Partial<Channels.Channel>,
+        clear?: RemoveChannelField,
+    ) {
+        const apply = (key: string) => {
+            // This code has been tested.
+            // @ts-expect-error
+            if (data[key] && !isEqual(this[key], data[key])) {
+                // @ts-expect-error
+                this[key] = data[key];
+            }
+        };
+
+        switch (clear) {
+            case "Description":
+                this.description = null;
+                break;
+            case "Icon":
+                this.icon = null;
+                break;
+        }
+
+        apply("active");
+        apply("owner");
+        apply("permissions");
+        apply("default_permissions");
+        apply("role_permissions");
+        apply("name");
+        apply("icon");
+        apply("description");
+        apply("recipients");
+        apply("last_message");
+    }
+}
+
 export class DataStore {
     @observable users = new Map<string, User>();
+    @observable channels = new Map<string, Channel>();
 
     constructor() {
         makeAutoObservable(this);
@@ -84,6 +187,11 @@ export class DataStore {
                 for (let user of packet.users) {
                     this.users.set(user._id, new User(user));
                 }
+
+                for (let channel of packet.channels) {
+                    this.channels.set(channel._id, new Channel(channel));
+                }
+
                 break;
             }
             case "UserUpdate": {
diff --git a/src/pages/Open.tsx b/src/pages/Open.tsx
index 6a36e24605d9f9a099f44851578050bcdac7b63a..ddeb98b684c6f4db372c24f094b3ca000ebc9464 100644
--- a/src/pages/Open.tsx
+++ b/src/pages/Open.tsx
@@ -9,11 +9,6 @@ import {
     ClientStatus,
     StatusContext,
 } from "../context/revoltjs/RevoltClient";
-import {
-    useChannels,
-    useForceUpdate,
-    useUser,
-} from "../context/revoltjs/hooks";
 
 import Header from "../components/ui/Header";
 
@@ -32,13 +27,9 @@ export default function Open() {
         );
     }
 
-    const ctx = useForceUpdate();
-    const channels = useChannels(undefined, ctx);
-    const user = useUser(id, ctx);
-
     useEffect(() => {
         if (id === "saved") {
-            for (const channel of channels) {
+            for (const channel of client.channels.toArray()) {
                 if (channel?.channel_type === "SavedMessages") {
                     history.push(`/channel/${channel._id}`);
                     return;
@@ -53,12 +44,15 @@ export default function Open() {
             return;
         }
 
+        let user = client.users.get(id);
         if (user) {
-            const channel: string | undefined = channels.find(
-                (channel) =>
-                    channel?.channel_type === "DirectMessage" &&
-                    channel.recipients.includes(id),
-            )?._id;
+            const channel: string | undefined = client.channels
+                .toArray()
+                .find(
+                    (channel) =>
+                        channel?.channel_type === "DirectMessage" &&
+                        channel.recipients.includes(id),
+                )?._id;
 
             if (channel) {
                 history.push(`/channel/${channel}`);
diff --git a/src/pages/settings/server/Invites.tsx b/src/pages/settings/server/Invites.tsx
index e77bf3b5dba2b694dba4c28167b8e2f76bc170e1..1708d851b1a87ecff828cec25094d1f67bda5b68 100644
--- a/src/pages/settings/server/Invites.tsx
+++ b/src/pages/settings/server/Invites.tsx
@@ -9,11 +9,7 @@ import { useEffect, useState } from "preact/hooks";
 import { useData } from "../../../mobx/State";
 
 import { useClient } from "../../../context/revoltjs/RevoltClient";
-import {
-    useChannels,
-    useForceUpdate,
-    useUsers,
-} from "../../../context/revoltjs/hooks";
+import { useChannels, useForceUpdate } from "../../../context/revoltjs/hooks";
 import { getChannelName } from "../../../context/revoltjs/util";
 
 import UserIcon from "../../../components/common/user/UserIcon";
@@ -34,7 +30,6 @@ export const Invites = observer(({ server }: Props) => {
     const channels = useChannels(invites?.map((x) => x.channel) ?? [], ctx);
 
     const store = useData();
-    const client = useClient();
     const users = invites?.map((invite) => store.users.get(invite.creator));
 
     useEffect(() => {
diff --git a/src/pages/settings/server/Members.tsx b/src/pages/settings/server/Members.tsx
index 24528cba3b07c2b96e419a7fa0a58048169007e4..69f3aa97f252707814249b73923de059abc46149 100644
--- a/src/pages/settings/server/Members.tsx
+++ b/src/pages/settings/server/Members.tsx
@@ -10,7 +10,6 @@ import { useEffect, useState } from "preact/hooks";
 import { useData } from "../../../mobx/State";
 
 import { useClient } from "../../../context/revoltjs/RevoltClient";
-import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
 
 import UserIcon from "../../../components/common/user/UserIcon";
 import Button from "../../../components/ui/Button";