useMemo , React.memo , useCallback

KHW·2021년 10월 11일
0

프레임워크

목록 보기
17/43

컴포넌트 랜더링

  • 컴포넌트가 랜더링 된다 -> 함수가 호출 -> 다시 연산 / 함수도 다시 정의(연산)
  1. 함수형 Component는 jsx를 반환한다.
  2. Component가 렌더링 된다는 것은 해당 Component를 호출했다는 것, 그렇다면 기본적으로 선언된 변수나 함수는 초기화된다.
  3. Component에서 State 값이 변경되면, 해당 Component(부모)와 하위 Component(자식)는 재 렌더링 된다.

useMemo / useCallback

렌더링에 영향을 받는 것이 2번째 매개변수의 변화에만 영향을 받아 처리

  • useCallback(fn, deps)은 useMemo(() => fn, deps)와 같습니다.

  • 사용이유는 어떤 컴포넌트 렌더링에 영향을 받아 매번 같은 결과를 반환하는 부분을 처리하는 것을 막기 위해서 (ex 심각한 counting 여러번 하는 것까지 )

1. useMemo

  • App.js
import react, { useState, useMemo, useCallback } from "react";
import User from './components/Box/index.js'

export default function App() {
  console.log("부모!");

  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  //기존 유저 이름 변경 함수
  const onChangeName = (e) => {
    const data = e.target.value;
    console.log("data : ", data);

    setName(data);
  };

  //기존 유저 이름 변경 함수
  const onChangeAge = (e) => {
    const age = e.target.value;
    console.log("age : ", age);

    setAge(age);
  };

  return (
    <div className="App">
      <h1>부모 컴포넌트</h1>

      <User
        name={name}
        age={age}
        onChangeName={onChangeName}
        onChangeAge={onChangeAge}
      />
    </div>
  );
}
  • index.js
import react, { useMemo, useCallback } from "react";

const setNameTag = (name) => {
  console.log("이름 태그!!!!!!!!!!!!!!!!!!!!!!!");

  return `이름은 : ${name}`;
};

const setAgeTag = (name) => {
  console.log("나이 태그~~~");

  return `나이는 : ${name}`;
};

const User = (props) => {
  console.log("자식");

  //일반적인 props 받은 값
  const nameFunc =setNameTag(props.name)
  const ageFunc =setAgeTag(props.age)
  const naem = useMemo(()=>nameFunc, [props.name]);
  const age = useMemo(()=>ageFunc, [props.age]);

  // const nameFunc =setNameTag(props.name)
  // const ageFunc =setAgeTag(props.age)
  // const naem = useCallback(nameFunc, [props.name]);
  // const age = useCallback(ageFunc, [props.age]);

  return (
    <div>
      <h1>{naem}</h1>
      <h1>{age}</h1>
      <div>
        <input type="text" onChange={(e) => props.onChangeName(e)} />
      </div>

      <div>
        <input type="text" onChange={(e) => props.onChangeAge(e)} />
      </div>
    </div>
  );
};

export default User

2. useCallback

  • App.js
import react, { useState, useMemo, useCallback } from "react";
import User from './components/Box/index.js'

export default function App() {
  console.log("부모!");

  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  //기존 유저 이름 변경 함수
  const onChangeName = (e) => {
    const data = e.target.value;
    console.log("data : ", data);

    setName(data);
  };

  //기존 유저 이름 변경 함수
  const onChangeAge = (e) => {
    const age = e.target.value;
    console.log("age : ", age);

    setAge(age);
  };

  return (
    <div className="App">
      <h1>부모 컴포넌트</h1>

      <User
        name={name}
        age={age}
        onChangeName={onChangeName}
        onChangeAge={onChangeAge}
      />
    </div>
  );
}
  • index.js
import react, { useMemo, useCallback } from "react";

const setNameTag = (name) => {
  console.log("이름 태그!!!!!!!!!!!!!!!!!!!!!!!");

  return `이름은 : ${name}`;
};

const setAgeTag = (name) => {
  console.log("나이 태그~~~");

  return `나이는 : ${name}`;
};

const User = (props) => {
  console.log("자식");

  //일반적인 props 받은 값
  // const nameFunc =setNameTag(props.name)
  // const ageFunc =setAgeTag(props.age)
  // const naem = useMemo(()=>nameFunc, [props.name]);
  // const age = useMemo(()=>ageFunc, [props.age]);
  //
  const nameFunc =setNameTag(props.name)
  const ageFunc =setAgeTag(props.age)
  const naem = useCallback(nameFunc, [props.name]);
  const age = useCallback(ageFunc, [props.age]);

  return (
    <div>
      <h1>{naem}</h1>
      <h1>{age}</h1>
      <div>
        <input type="text" onChange={(e) => props.onChangeName(e)} />
      </div>

      <div>
        <input type="text" onChange={(e) => props.onChangeAge(e)} />
      </div>
    </div>
  );
};

export default User

두 차이는 useMemo에서는 useMemo(()=>nameFunc, [props.name]);를 사용하여 return된 이 이용되고
useCallback에서는 useCallback(nameFunc, [props.name]); 콜백 함수로 매개변수가 이용된다.

React.memo / useCallback

함수가 재정의 되면서 새로운 메모리를 할당될 함수가 어떤 이벤트로 넘어가면 새롭게 정의되어 이는 React.memo로 막을 수 없는 부분으로 useCallback이 담당해야한다.

1. 다른 부모 컴포넌트에 영향을 받는 자식 컴포넌트

  • App.js
import react, { useState, useMemo, useCallback } from "react";
import User from './components/Box/index.js'

export default function App() {
  console.log("부모!");
  const [count , setCount] = useState(0);
  const [name, setName] = useState("");
  const [age, setAge] = useState("");
  return (
    <div className="App">
      <button onClick={()=>setCount(count+1)}>+</button>
      <h1>부모 컴포넌트</h1>
      <User/>
    </div>
  );
}
  • index.js
import react, { useMemo, useCallback } from "react";
import React from "react"

const User = ((props) => {
  console.log("자식");
  return (
    <div>
      <h1>{props.name}</h1>
      <h1>{props.age}</h1>
      <div>
        <input type="text" onChange={(e) => props.onChangeName(e)} />
      </div>

      <div>
        <input type="text" onChange={(e) => props.onChangeAge(e)} />
      </div>
    </div>
  );
});

export default User

버튼을 누르면 결과는 부모 자식 전부 출력된다.
즉, 버튼과 상관없는 컴포넌트의 자식태그가 영향을 받아 자식을 출력한다.

2. 다른 부모 컴포넌트에 영향을 받지않는 자식 컴포넌트

React.memo로 index.js의 const User를 묶으면
버튼을 누르면 부모만 출력된다.
즉, 다른 부모 컴포넌트에 영향을 받지않는 자식 컴포넌트가 된다.

3. 부모 컴포넌트에 이벤트가 있는 자식 컴포넌트

  • App.js
import react, { useState, useMemo, useCallback } from "react";
import User from './components/Box/index.js'

export default function App() {
  console.log("부모!");
  const [count , setCount] = useState(0);
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  //기존 유저 이름 변경 함수
  const onChangeName = useCallback((e) => {
    const data = e.target.value;
    console.log("data : ", data);
    setName(data);
  },[]);

  //기존 유저 이름 변경 함수
  const onChangeAge = useCallback((e) => {
    const age = e.target.value;
    console.log("age : ", age);
    setAge(age);
  });

  return (
    <div className="App">
      <button onClick={()=>setCount(count+1)}>+</button>
      <h1>부모 컴포넌트</h1>
      <User
        onChangeAge={onChangeAge}
        onChangeName={onChangeName}
      />
    </div>
  );
}

부모 컴포넌트에 onChangeAge와 onChangeName 이벤트가 있는 와중에
React.memo는 유지한 상태로 버튼을 누르면 부모 자식 전부 출력된다.

4. useCallback으로 이벤트 영향 해결하기

원인 : 함수(컴포넌트)가 재정의 되면서 새로운 메모리 할당될 함수가 onchange로 넘어와서 이를 통해 다시 User 컴포넌트가 렌더링이 된다.

  • useCallback은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용
  • 즉, 특정함수가 만들어지지 않으므로 컴포넌트가 따로 렌더링 되지않는다.
  • App.js
import react, { useState, useMemo, useCallback } from "react";
import User from './components/Box/index.js'

export default function App() {
  console.log("부모!");
  const [count , setCount] = useState(0);
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  //기존 유저 이름 변경 함수
  const onChangeName = useCallback((e) => {
    const data = e.target.value;
    console.log("data : ", data);
    setName(data);
  },[]);

  //기존 유저 이름 변경 함수
  const onChangeAge = useCallback((e) => {
    const age = e.target.value;
    console.log("age : ", age);
    setAge(age);
  },[]);

  return (
    <div className="App">
      <button onClick={()=>setCount(count+1)}>+</button>
      <h1>부모 컴포넌트</h1>
      <User
        onChangeAge={onChangeAge}
        onChangeName={onChangeName}
      />
    </div>
  );
}

해당 원인이었던 onChangeAge와 onChangeName을 useCallback을 이용하여
버튼을 눌렀을때에도 부모만 출력된다.

총정리 (중요)

  1. 부모 state가 변경되어도 자식 컴포넌트에 영향을 준다.
  2. 그것을 막기 위해서 자식 컴포넌트React.memo로 묶어서 영향을 막는다
  3. 하지만 해당 컴포넌트에 이벤트가 걸려져 있다면 함수가 재정의 되면서 새로운 메모리가 할당되고 이는 다시 영향을 준다.
  4. 이것을 막기 위해 useCallback을 사용하여 특정 함수를 새로 만들지 않고 재사용하므로 영향을 막는다.
  5. 그외에도 계산을 길게하는 함수를 통한 리턴 값을 매번 영향을 받는다고 동작시키는 것을 막기 위해 성능 최적화를 위하여 연산된 값을 가지는 useMemo를 사용한다.
profile
나의 하루를 가능한 기억하고 즐기고 후회하지말자

0개의 댓글