diff --git a/webapp/channels/src/components/pdf_preview.tsx b/webapp/channels/src/components/pdf_preview.tsx
index 8bf50ac4728..8c702f52801 100644
--- a/webapp/channels/src/components/pdf_preview.tsx
+++ b/webapp/channels/src/components/pdf_preview.tsx
@@ -6,12 +6,10 @@ import type {PDFDocumentProxy, PDFPageProxy} from 'pdfjs-dist';
 import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs';
 import 'pdfjs-dist/build/pdf.worker.min.mjs';
 import type {RenderParameters} from 'pdfjs-dist/types/src/display/api';
-import React from 'react';
+import React, {useRef, useState, memo, useEffect, useCallback} from 'react';
 
 import type {FileInfo} from '@mattermost/types/files';
 
-import {getFileDownloadUrl} from 'mattermost-redux/utils/file_utils';
-
 import FileInfoPreview from 'components/file_info_preview';
 import LoadingSpinner from 'components/widgets/loading/loading_spinner';
 
@@ -34,99 +32,115 @@ export type Props = {
     handleBgClose: (e: React.MouseEvent<Element, MouseEvent>) => void;
 }
 
-type State = {
-    pdf: PDFDocumentProxy | null;
-    pdfPages: Record<number, PDFPageProxy>;
-    pdfPagesLoaded: Record<number, boolean>;
-    numPages: number;
-    loading: boolean;
-    success: boolean;
-    prevFileUrl: string;
-}
+type Status = 'success' | 'loading' | 'fail';
+
+const PDFPreview = memo(({
+    fileInfo,
+    fileUrl,
+    scale,
+    handleBgClose,
+}: Props) => {
+    const pdfPagesRendered = useRef<Record<number, boolean>>({});
+    const container = useRef<HTMLDivElement>(null);
+    const parentNode = useRef<HTMLElement | null>(null);
+    const pdfCanvasRef = useRef<{[key: string]: React.RefObject<HTMLCanvasElement>}>({});
+
+    const [pdfFromState, setPdfFromState] = useState<{
+        pdf: PDFDocumentProxy | null;
+        pages: Record<number, PDFPageProxy>;
+        pagesLoaded: Record<number, boolean>;
+        numPages: number;
+    }>({
+        pdf: null,
+        pages: {},
+        pagesLoaded: {},
+        numPages: 0,
+    });
+
+    const [status, setStatus] = useState<Status>('loading');
+
+    useEffect(() => {
+        setPdfFromState({
+            pdf: null,
+            pages: {},
+            pagesLoaded: {},
+            numPages: 0,
+        });
 
-export default class PDFPreview extends React.PureComponent<Props, State> {
-    public pdfPagesRendered: Record<number, boolean>;
-    public container: React.RefObject<HTMLDivElement>;
-    public parentNode: HTMLElement|null = null;
-    public pdfCanvasRef: {[key: string]: React.RefObject<HTMLCanvasElement>} = {};
+        setStatus('loading');
 
-    constructor(props: Props) {
-        super(props);
+        const onDocumentLoad = (pdf: PDFDocumentProxy) => {
+            setPdfFromState((prev) => {
+                return {
+                    ...prev,
+                    pdf,
+                    numPages: pdf.numPages,
+                };
+            });
 
-        this.pdfPagesRendered = {};
-        this.container = React.createRef();
+            for (let i = 0; i < pdf.numPages; i++) {
+                pdfCanvasRef.current[`pdfCanvasRef-${i}`] = React.createRef();
+            }
 
-        this.state = {
-            pdf: null,
-            pdfPages: [],
-            pdfPagesLoaded: {},
-            numPages: 0,
-            loading: true,
-            success: false,
-            prevFileUrl: '',
+            setStatus('success');
         };
-    }
 
-    componentDidMount() {
-        this.getPdfDocument();
-        if (this.container.current) {
-            this.parentNode = this.container.current.parentElement;
-            this.parentNode?.addEventListener('scroll', this.handleScroll);
-        }
-    }
+        const onDocumentLoadError = (reason: string) => {
+            console.log('Unable to load PDF preview: ' + reason); //eslint-disable-line no-console
 
-    componentWillUnmount() {
-        if (this.parentNode) {
-            this.parentNode.removeEventListener('scroll', this.handleScroll);
-        }
-    }
-
-    static getDerivedStateFromProps(props: Props, state: State) {
-        if (props.fileUrl !== state.prevFileUrl) {
-            return {
-                pdf: null,
-                pdfPages: {},
-                pdfPagesLoaded: {},
-                numPages: 0,
-                loading: true,
-                success: false,
-                prevFileUrl: props.fileUrl,
-            };
-        }
-        return null;
-    }
+            setStatus('fail');
+        };
 
-    componentDidUpdate(prevProps: Props, prevState: State) {
-        if (this.props.fileUrl !== prevProps.fileUrl) {
-            this.getPdfDocument();
-            this.pdfPagesRendered = {};
-        }
-        if (this.props.scale !== prevProps.scale) {
-            this.pdfPagesRendered = {};
-            if (this.state.success) {
-                for (let i = 0; i < this.state.numPages; i++) {
-                    this.renderPDFPage(i);
-                }
+        const getPdfDocument = async () => {
+            try {
+                const pdf = await pdfjsLib.getDocument({
+                    url: fileUrl,
+                    cMapUrl: getSiteURL() + '/static/cmaps/',
+                    cMapPacked: true,
+                }).promise;
+                onDocumentLoad(pdf);
+            } catch (err) {
+                onDocumentLoadError(String(err));
             }
-        }
+        };
 
-        if (!prevState.success && this.state.success) {
-            for (let i = 0; i < this.state.numPages; i++) {
-                this.renderPDFPage(i);
-            }
+        getPdfDocument();
+
+        /**
+         * This is already initialized to an empty object above, so on mount, it's set to the same
+         * initial empty object. Same for every update.
+         */
+        pdfPagesRendered.current = {};
+    }, [fileUrl]);
+
+    const loadPage = useCallback(async (pdf: PDFDocumentProxy, pageIndex: number) => {
+        if (pdfFromState.pagesLoaded[pageIndex]) {
+            return pdfFromState.pages[pageIndex];
         }
-    }
 
-    downloadFile = (e: React.FormEvent) => {
-        const fileDownloadUrl = this.props.fileInfo.link || getFileDownloadUrl(this.props.fileInfo.id);
-        e.preventDefault();
-        window.location.href = fileDownloadUrl;
-    };
+        const page = await pdf.getPage(pageIndex + 1);
 
-    isInViewport = (page: Element) => {
+        setPdfFromState((prev) => {
+            return {
+                ...prev,
+                pages: {
+                    ...prev.pages,
+                    [pageIndex]: page,
+                },
+                pagesLoaded: {
+                    ...prev.pagesLoaded,
+                    [pageIndex]: true,
+                },
+            };
+        });
+
+        return page;
+    }, [pdfFromState.pages, pdfFromState.pagesLoaded]);
+
+    const isInViewport = (page: Element) => {
         const bounding = page.getBoundingClientRect();
-        const viewportTop = this.container.current?.scrollTop ?? 0;
-        const viewportBottom = viewportTop + (this.parentNode?.clientHeight ?? 0);
+        const viewportTop = container.current?.scrollTop ?? 0;
+        const viewportBottom = viewportTop + (parentNode.current?.clientHeight ?? 0);
         return (
             (bounding.top >= viewportTop && bounding.top <= viewportBottom) ||
             (bounding.bottom >= viewportTop && bounding.bottom <= viewportBottom) ||
@@ -134,8 +148,8 @@ export default class PDFPreview extends React.PureComponent<Props, State> {
         );
     };
 
-    renderPDFPage = async (pageIndex: number) => {
-        const canvas = this.pdfCanvasRef[`pdfCanvasRef-${pageIndex}`].current;
+    const renderPDFPage = useCallback(async (pageIndex: number) => {
+        const canvas = pdfCanvasRef.current[`pdfCanvasRef-${pageIndex}`].current;
         if (!canvas) {
             // Refs are undefined when testing
             return;
@@ -143,17 +157,17 @@ export default class PDFPreview extends React.PureComponent<Props, State> {
 
         // Always render the first INITIAL_RENDERED_PAGES pages to avoid
         // problems detecting isInViewport during the open animation
-        if (pageIndex >= INITIAL_RENDERED_PAGES && !this.isInViewport(canvas)) {
+        if (pageIndex >= INITIAL_RENDERED_PAGES && !isInViewport(canvas)) {
             return;
         }
 
-        if (this.pdfPagesRendered[pageIndex]) {
+        if (pdfPagesRendered.current[pageIndex]) {
             return;
         }
 
-        const page = await this.loadPage(this.state.pdf!, pageIndex);
+        const page = await loadPage(pdfFromState.pdf!, pageIndex);
         const context = canvas.getContext('2d');
-        const viewport = page.getViewport({scale: this.props.scale});
+        const viewport = page.getViewport({scale});
         canvas.height = viewport.height;
         canvas.width = viewport.width;
 
@@ -163,108 +177,110 @@ export default class PDFPreview extends React.PureComponent<Props, State> {
         } as RenderParameters;
 
         await page.render(renderContext).promise;
-        this.pdfPagesRendered[pageIndex] = true;
-    };
+        pdfPagesRendered.current[pageIndex] = true;
+    }, [loadPage, pdfFromState.pdf, scale]);
+
+    useEffect(() => {
+        const handleScroll = debounce(() => {
+            if (status === 'success') {
+                for (let i = 0; i < pdfFromState.numPages; i++) {
+                    renderPDFPage(i);
+                }
+            }
+        }, 100);
 
-    getPdfDocument = async () => {
-        try {
-            const pdf = await pdfjsLib.getDocument({
-                url: this.props.fileUrl,
-                cMapUrl: getSiteURL() + '/static/cmaps/',
-                cMapPacked: true,
-            }).promise;
-            this.onDocumentLoad(pdf);
-        } catch (err) {
-            this.onDocumentLoadError(err);
+        if (container.current) {
+            parentNode.current = container.current.parentElement;
+            parentNode.current?.addEventListener('scroll', handleScroll);
         }
-    };
 
-    onDocumentLoad = (pdf: PDFDocumentProxy) => {
-        this.setState({pdf, numPages: pdf.numPages});
-        for (let i = 0; i < pdf.numPages; i++) {
-            this.pdfCanvasRef[`pdfCanvasRef-${i}`] = React.createRef();
-        }
-        this.setState({loading: false, success: true});
-    };
+        return () => {
+            handleScroll.cancel();
 
-    onDocumentLoadError = (reason: string) => {
-        console.log('Unable to load PDF preview: ' + reason); //eslint-disable-line no-console
-        this.setState({loading: false, success: false});
-    };
+            if (parentNode.current) {
+                parentNode.current.removeEventListener('scroll', handleScroll);
+            }
+        };
+    }, [pdfFromState.numPages, renderPDFPage, status]);
 
-    loadPage = async (pdf: PDFDocumentProxy, pageIndex: number) => {
-        if (this.state.pdfPagesLoaded[pageIndex]) {
-            return this.state.pdfPages[pageIndex];
+    const prevScale = useRef(scale);
+    useEffect(() => {
+        // Only re-render the pages when the user zooms in or out
+        if (scale === prevScale.current) {
+            return;
         }
 
-        const page = await pdf.getPage(pageIndex + 1);
-
-        const pdfPages = Object.assign({}, this.state.pdfPages);
-        pdfPages[pageIndex] = page;
-
-        const pdfPagesLoaded = Object.assign({}, this.state.pdfPagesLoaded);
-        pdfPagesLoaded[pageIndex] = true;
-        this.setState({pdfPages, pdfPagesLoaded});
+        prevScale.current = scale;
 
-        return page;
-    };
+        pdfPagesRendered.current = {};
 
-    handleScroll = debounce(() => {
-        if (this.state.success) {
-            for (let i = 0; i < this.state.numPages; i++) {
-                this.renderPDFPage(i);
+        if (status === 'success') {
+            for (let i = 0; i < pdfFromState.numPages; i++) {
+                renderPDFPage(i);
             }
         }
-    }, 100);
+    }, [pdfFromState.numPages, renderPDFPage, scale, status]);
 
-    render() {
-        if (this.state.loading) {
-            return (
-                <div
-                    ref={this.container}
-                    className='view-image__loading'
-                >
-                    <LoadingSpinner/>
-                </div>
-            );
-        }
-
-        if (!this.state.success) {
-            return (
-                <FileInfoPreview
-                    fileInfo={this.props.fileInfo}
-                    fileUrl={this.props.fileUrl}
-                />
-            );
-        }
-
-        const pdfCanvases = [];
-        for (let i = 0; i < this.state.numPages; i++) {
-            pdfCanvases.push(
-                <canvas
-                    ref={this.pdfCanvasRef[`pdfCanvasRef-${i}`]}
-                    key={'previewpdfcanvas' + i}
-                />,
-            );
-
-            if (i < this.state.numPages - 1 && this.state.numPages > 1) {
-                pdfCanvases.push(
-                    <div
-                        key={'previewpdfspacer' + i}
-                        className='pdf-preview-spacer'
-                    />,
-                );
+    const prevStatus = useRef<Status>(status);
+    useEffect(() => {
+        if (prevStatus.current !== status && status === 'success') {
+            for (let i = 0; i < pdfFromState.numPages; i++) {
+                renderPDFPage(i);
             }
         }
 
+        prevStatus.current = status;
+    }, [pdfFromState.numPages, renderPDFPage, status]);
+
+    if (status === 'loading') {
         return (
             <div
-                ref={this.container}
-                className='post-code'
-                onClick={this.props.handleBgClose}
+                ref={container}
+                className='view-image__loading'
             >
-                {pdfCanvases}
+                <LoadingSpinner/>
             </div>
         );
     }
-}
+
+    if (status === 'fail') {
+        return (
+            <FileInfoPreview
+                fileInfo={fileInfo}
+                fileUrl={fileUrl}
+            />
+        );
+    }
+
+    const pdfCanvases = [];
+    for (let i = 0; i < pdfFromState.numPages; i++) {
+        pdfCanvases.push(
+            <canvas
+                ref={pdfCanvasRef.current[`pdfCanvasRef-${i}`]}
+                key={'previewpdfcanvas' + i}
+            />,
+        );
+
+        if (i < pdfFromState.numPages - 1 && pdfFromState.numPages > 1) {
+            pdfCanvases.push(
+                <div
+                    key={'previewpdfspacer' + i}
+                    className='pdf-preview-spacer'
+                />,
+            );
+        }
+    }
+
+    return (
+        <div
+            ref={container}
+            className='post-code'
+            data-testid='pdf-container'
+            onClick={handleBgClose}
+        >
+            {pdfCanvases}
+        </div>
+    );
+});
+
+export default PDFPreview;
diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.test.ts
index 73594136ece..bafb796ceff 100644
--- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.test.ts
+++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.test.ts
@@ -41,6 +41,8 @@ describe('Selectors.Users', () => {
     const user7 = TestHelper.fakeUserWithId();
     user7.delete_at = 1;
     user7.roles = 'system_admin system_user';
+    const user8 = TestHelper.fakeUserWithId();
+    user8.nickname = 'vertex_to_edge';
     const profiles: Record<string, UserProfile> = {};
     profiles[user1.id] = user1;
     profiles[user2.id] = user2;
@@ -49,6 +51,7 @@ describe('Selectors.Users', () => {
     profiles[user5.id] = user5;
     profiles[user6.id] = user6;
     profiles[user7.id] = user7;
+    profiles[user8.id] = user8;
 
     const profilesInTeam: Record<Team['id'], Set<UserProfile['id']>> = {};
     profilesInTeam[team1.id] = new Set([user1.id, user2.id, user7.id]);
@@ -75,7 +78,7 @@ describe('Selectors.Users', () => {
     profilesNotInTeam[team1.id] = new Set([user3.id, user4.id]);
 
     const profilesInChannel: Record<Channel['id'], Set<UserProfile['id']>> = {};
-    profilesInChannel[channel1.id] = new Set([user1.id]);
+    profilesInChannel[channel1.id] = new Set([user1.id, user8.id]);
     profilesInChannel[channel2.id] = new Set([user1.id, user2.id]);
 
     const membersInChannel: Record<Channel['id'], Record<UserProfile['id'], ChannelMembership>> = {};
@@ -85,6 +88,10 @@ describe('Selectors.Users', () => {
         scheme_user: true,
         scheme_admin: true,
     };
+    membersInChannel[channel1.id][user8.id] = {
+        ...TestHelper.fakeChannelMember(user8.id, channel1.id),
+        scheme_user: true,
+    };
     membersInChannel[channel2.id] = {};
     membersInChannel[channel2.id][user1.id] = {
         ...TestHelper.fakeChannelMember(user1.id, channel2.id),
@@ -264,7 +271,7 @@ describe('Selectors.Users', () => {
 
     describe('getProfiles', () => {
         it('getProfiles without filter', () => {
-            const users = [user1, user2, user3, user4, user5, user6, user7].sort(sortByUsername);
+            const users = [user1, user2, user3, user4, user5, user6, user7, user8].sort(sortByUsername);
             expect(Selectors.getProfiles(testState)).toEqual(users);
         });
 
@@ -277,7 +284,7 @@ describe('Selectors.Users', () => {
             expect(Selectors.getProfiles(testState, {inactive: true})).toEqual(users);
         });
         it('getProfiles with active', () => {
-            const users = [user1, user3, user4, user5, user6].sort(sortByUsername);
+            const users = [user1, user3, user4, user5, user6, user8].sort(sortByUsername);
             expect(Selectors.getProfiles(testState, {active: true})).toEqual(users);
         });
         it('getProfiles with multiple filters', () => {
@@ -465,6 +472,7 @@ describe('Selectors.Users', () => {
         expect(Selectors.searchProfilesInCurrentChannel(testState, user1.username)).toEqual([user1]);
         expect(Selectors.searchProfilesInCurrentChannel(testState, 'engineer at mattermost')).toEqual([user1]);
         expect(Selectors.searchProfilesInCurrentChannel(testState, user1.username, true)).toEqual([]);
+        expect(Selectors.searchProfilesInCurrentChannel(testState, 'to_edge', true)).toEqual([user8]);
     });
 
     it('searchProfilesNotInCurrentChannel', () => {
@@ -525,7 +533,7 @@ describe('Selectors.Users', () => {
 
     it('makeGetProfilesInChannel', () => {
         const getProfilesInChannel = Selectors.makeGetProfilesInChannel();
-        expect(getProfilesInChannel(testState, channel1.id)).toEqual([user1]);
+        expect(getProfilesInChannel(testState, channel1.id)).toEqual([user1, user8]);
 
         const users = [user1, user2].sort(sortByUsername);
         expect(getProfilesInChannel(testState, channel2.id)).toEqual(users);
@@ -555,8 +563,8 @@ describe('Selectors.Users', () => {
         };
 
         const getProfilesInChannel = Selectors.makeGetProfilesInChannel();
-        expect(getProfilesInChannel(state, channel1.id)).toEqual([user1]);
-        expect(getProfilesInChannel(state, channel1.id, {})).toEqual([user1]);
+        expect(getProfilesInChannel(state, channel1.id)).toEqual([user1, user8]);
+        expect(getProfilesInChannel(state, channel1.id, {})).toEqual([user1, user8]);
     });
 
     it('makeGetProfilesNotInChannel', () => {
@@ -874,7 +882,7 @@ describe('Selectors.Users', () => {
 
     describe('filterProfiles', () => {
         it('no filter, return all users', () => {
-            expect(Object.keys(Selectors.filterProfiles(profiles)).length).toEqual(7);
+            expect(Object.keys(Selectors.filterProfiles(profiles)).length).toEqual(8);
         });
 
         it('filter role', () => {
@@ -901,7 +909,7 @@ describe('Selectors.Users', () => {
             const filter = {
                 exclude_roles: ['system_admin'],
             };
-            expect(Object.keys(Selectors.filterProfiles(profiles, filter)).length).toEqual(4);
+            expect(Object.keys(Selectors.filterProfiles(profiles, filter)).length).toEqual(5);
         });
 
         it('exclude bots', () => {
@@ -920,7 +928,7 @@ describe('Selectors.Users', () => {
                 ...profiles,
                 [botUser.id]: botUser,
             };
-            expect(Object.keys(Selectors.filterProfiles(newProfiles, filter)).length).toEqual(7);
+            expect(Object.keys(Selectors.filterProfiles(newProfiles, filter)).length).toEqual(8);
         });
 
         it('filter inactive', () => {
@@ -934,7 +942,7 @@ describe('Selectors.Users', () => {
             const filter = {
                 active: true,
             };
-            expect(Object.keys(Selectors.filterProfiles(profiles, filter)).length).toEqual(5);
+            expect(Object.keys(Selectors.filterProfiles(profiles, filter)).length).toEqual(6);
         });
     });
 });
diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts
index eb8faafeebc..319cac0ac9a 100644
--- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts
+++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/users.ts
@@ -510,7 +510,7 @@ export function makeSearchProfilesInChannel() {
 }
 
 export function searchProfilesInCurrentChannel(state: GlobalState, term: string, skipCurrent = false): UserProfile[] {
-    const profiles = filterProfilesStartingWithTerm(getProfilesInCurrentChannel(state), term);
+    const profiles = filterProfilesMatchingWithTerm(getProfilesInCurrentChannel(state), term);
 
     if (skipCurrent) {
         removeCurrentUserFromList(profiles, getCurrentUserId(state));
