JS Library/React

React.memo

원2 2022. 5. 25. 10:47
728x90
반응형

React.memo 는 컴포넌트의 props 가 변하지 않았으면 리렌더링을 방지해서 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있음

컴포넌트에서 리렌더링이 필요한 상황에서만 리렌더링을 하도록 설정 할 수 있음

 

 

CreateUser.js

 

import React from "react";

const CreateUser = ({username, email, onChange, onCreate}) => {
    return (
        <div>
            <input
                name="username"
                placeholder="계정명"
                onChange={onChange}
                value={username}
            />
            <input
                name="email"
                placeholder="이메일"
                onChange={onChange}
                value={email}
            />
            <button onClick={onCreate}>등록</button>
        </div>
    );
};

export default React.memo(CreateUser);

function > const 로 변경후 감싸주면 됨

 

export default 에 CreateUser 를 React.memo 로 감싸주기만 하면 됨

 


UserList.js 도 해주자

 

import React, {useEffect} from "react";

const User = React.memo(function User({user, onRemove, onToggle}) {

    useEffect(() => {
        console.log(user);
    });

    return (
        <div>
            <b
                style={{
                    cursor: 'pointer',
                    color: user.active ? 'green' : 'black'
                }}
                onClick={() => onToggle(user.id)}
            >
                {user.username}
            </b>
            <span>({user.email})</span>
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
});

// 순서상 UserList 에서 on Toggle 을 받아오고, User 에서 호출
function UserList({users, onRemove, onToggle}) {
    return (
        <div>
            {users.map(user => (
                <User
                    user={user}
                    Key={user.id}
                    onRemove={onRemove}
                    onToggle={onToggle}

                />
            ))}
        </div>
    );
}

export default React.memo(UserList);

마찬가지로 function > const 변경후 감싸주기

react 개발자 도구를 보면 Memo 가 되어있다고 친절히 알려준다....

그리고 해당 input 값에 값을 변경시켜보면 UserList 들이 리렌더링이 되지 않는것을 확인 할 수 있음

 

그런데 User 중 하나라도 수정(클릭을 해서 false 를 ture 로 변경 한다던지..) 하면 모든 User 이 리렌더링 되고 CreateUser 도 리렌더링이 됨.

>> 이유 users 배열이 변경 될 때 마다 해당 props 를 포함하고 있는 함수들 onCreate, onToggle, onRemove 도 영향을 받아서 새로 만들어지기 때문 > deps 에 users 가 들어있기 때문

 

이걸 최적화 하려면 deps 에서 users 를 지우고 함수들에서 현재 useState 로 관리하는 users 를 참조하지 않게 해야함

>> 함수형 업데이트를 사용해서 setUsers 에 등록하는 콜백함수의 파라미터에서 최신 users 를 참조 할 수 있음

>> deps에 users 를 넣지 않아도 됌


App.js

onChange, onCreate, onToggle, onRemove 를 수정해보자 (onChange 는 users가 없어서 영향이 없지만 연습ㄱ)

import React, {useCallback, useMemo, useRef, useState} from 'react';
import CreateUser from './components/CreateUser';
import UserList from "./components/UserList";

function countActiveUsers(users) {
    console.log("active 값이 ture 인 유저의 수를 센는중...");
    return users.filter(user => user.active).length;
}

function App() {

    const [inputs, setInputs] = useState({
        username: '',
        email: ''
    });

    const {username, email} = inputs;

    const onChange = useCallback(
        e => {
            const {name, value} = e.target;
            setInputs(inputs => ({
                // spread
                ...inputs,
                [name]: value
            }));
        }, []);

    // useState 를 사용하여 컴포넌트의 상태로 관리
    const [users, setUsers] = useState([
        {
            id: 1,
            username: 'velopert',
            email: 'public.velopert@gmail.com',
            active: true
        },
        {
            id: 2,
            username: 'tester',
            email: 'tester@example.com',
            active: false
        },
        {
            id: 3,
            username: 'liz',
            email: 'liz@example.com',
            active: false
        }
    ]);


    const nextId = useRef(4);
    const onCreate = useCallback(() => {
        const user = {
            id: nextId.current,
            username,
            email
        };
        // concat 함수
        setUsers(users => users.concat(user));

        // spread 함수
        // setUsers([
        //     //spread 문법
        //     ...users,user
        // ]);

        setInputs({
            username: '',
            email: ''
        });

        nextId.current += 1;
    }, [username, email]);

    const onRemove = useCallback(
        id => {
            //    user.id 가 파라미터로 일치하지 않는 우너소만 추출해서 새로운 배열을 만듬
            //    = user.id 가 id 인 것을 제거함
            setUsers(users => users.filter(user => user.id !== id));
        }, []);


    const onToggle = useCallback(
        id => {
            setUsers(users =>
                users.map(user =>
                    user.id === id ? {...user, active: !user.active} : user
                )
            );
        }, []);

    const count = useMemo(() => countActiveUsers(users), [users]);

    return (
        <>
            <CreateUser
                username={username}
                email={email}
                onChange={onChange}
                onCreate={onCreate}
            />
            <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
            <div>활성 사용자 수 : {count}</div>
        </>
    );
}


export default App;

 

이렇게 하면 해당 항목만 리렌더링이 됌 굳ㅋ 최적화 끝

 

리액트 개발을 할 땐 useCallback, useMemo, React.Memo 는 컴포넌트의 성능을 실제로 개선할수 있는 상황에서만 사용하자

User 컴포넌트에 <b> 와 button 에 onClick 으로 설정해준 함수들은 useCallback 으로 재사용한다해서 리렌더링을 막을 수 있는 것이 아니라 할필요없음

 

또한 렌더링 최적화를 하지 않을 컴포넌트에 React.memo 를 사용하는 것은 불필요한 props 비교만 하는 것이라 실제 렌더링을 방지 할 경우에만 사용하자

 

++

React.memo 에서 두번째 파라미터에 propsAreEqual 함수를 사용하여 특정 값들만 비교를 하는것도 가능

export default React.memo(
    UserList,
    (prevProps, nextProps) => prevProps.users === nextProps.users
    );

근데 이거 잘못쓰면 버그 많이 생김

함수형 업데이트로 전환을 안했는데 이렇게 users 만 비교하면 onToggle, onRemove 에서 최선 users 배열을 참조하지 않아서 오류 발생가능성이 있다.

https://react.vlpt.us/basic/19-React.memo.html

728x90
반응형

'JS Library > React' 카테고리의 다른 글

Immer 라이브러리  (0) 2022.05.26
Context API 전역 값 관리  (0) 2022.05.26
커스텀 Hooks  (0) 2022.05.25
useCallback 를 사용하여 함수 재사용  (0) 2022.05.25
React Developer Tools  (0) 2022.05.24
useMemo 를 사용하여 연산한 값 재사용하기  (0) 2022.05.24