velopert / react-tutorial

벨로퍼트와 함께하는 모던 리액트 튜토리얼 문서
https://react.vlpt.us/
350 stars 101 forks source link

18. useCallback 를 사용하여 함수 재사용하기 · GitBook #10

Open utterances-bot opened 5 years ago

utterances-bot commented 5 years ago

18. useCallback 를 사용하여 함수 재사용하기 · GitBook

undefined

https://react.vlpt.us/basic/18-useCallback.html

hyunwoo86 commented 5 years ago

안녕하세요. 수업을 듣고 있는데 버전이 react devtools 버전이 변한거 같습니다. 따라서 리렌더링 될때 확인 하는 방법을 찾지 못하고 있습니다. 혹시 알려주시면 감사하겠습니다.

sunginHwang commented 5 years ago

@hyunwoo86 님 devtools 버전을 낮추어서 진행하시면 됩니다. 관련되서 https://blog.woolta.com/categories/1/posts/159 참고하시면 도움이 될거 같습니다. :)

GodChiken commented 5 years ago

devtools 4.2 기준으로 수정된거같아요

haaneul11 commented 4 years ago

useCallback을 사용하기 전과 사용한 후의 렌더링 박스 상태는 차이가 없는거 맞나요??!! 렌더링 확인하는건 CreateUser과 UserList에서의 차이인거죠??

pyeonjaesik commented 4 years ago

useCallback의 함수가 새로이 생성되는 것을 방지 하여 재사용하게끔 하는것은 어렴풋이 알겠습니다.

하지만 함수가 새로 생성되는것 자체에대해서 아리송하네요...

함수가 새로 생성된다는건 어떤 의미인가요?

hkhch commented 4 years ago

안녕하세요. 정리하신 내용 감사하게 보았습니다. ^^ 포스팅 내용 중에 궁금한게 있어 질문드립니다. const onCreate = useCallback(() => { const user = { username, age, id: nextId.current, active: false }; setUsers([...users, user]); setInputs({ username: "", age: "" }); nextId.current += 1; }, [users, username, age]);

  1. 위 코드에서 useCallback dependency 배열 부분에 nextId 변수는 왜 추가되지 않은 건가요? nextId도 useCallback 밖에서 변경되는 값이라면 dependency 배열에 추가해서 함수의 재생성 조건에 포함되어야 할 듯 해서요.

  2. 아래와 같이 밖에서 정의된 함수가 사용될때에 dependency 배열 부분에 추가해 줘야 하나요? (아래 예시와 같이 calculate 함수가 외부에서 선언/정의된 경우) const onHandle = useCallback(() => { ..... const value = calculate(100); ..... }, [calculate]);

sunginHwang commented 4 years ago

@hkhch 님

  1. nextId.current 인것으로 봐서 nextId 는 ref를 통한 값 조작으로 보입니다. ref 의 경우 현재 상태의 값으로만 보여주기때문에 dependency 에 넣지 않더라도 호출 시점 현재의 값으로 보여주기 때문에 안넣은것으로 보입니다.

  2. 해당 calculate도 dependency 영역에 포함된다면 (외부에서 선언/정의된 함수가 변경이 필요한 함수라면) 판단 하신후 넣어주시면 됩니다.~

cloudbaby1991 commented 3 years ago

궁금한점이 있습니다. 왜 다음의 본문 문장처럼 최신의 값을 바라보지 못한다고 하는 것인가요? 해당 값을 참조하면 해당 값을 바라보는게 아니였을까요.

/ 본문 내용 / 주의 하실 점은, 함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, deps 배열안에 포함시켜야 된다는 것 입니다. 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없습니다. props 로 받아온 함수가 있다면, 이 또한 deps 에 넣어주어야 해요.

mypiece commented 3 years ago

안녕하세요, "리액트를 다루는 기술" 개정판을 구입해서 보던 중 궁금증을 해결하지 못해 서칭하다 여기까지 오게되었습니다. 저도 결국 위 @cloudbaby1991 님과 동일한 점이 궁금한데요.

함수를 재사용하는게 중요한 것은 알겠는데 함수에 상태 혹은 props 가 있을 경우에는 왜 함수를 새로 생성해야 하는건가요? 함수를 재사용한다는 것이 결국 동일한 내용을 함수를 재호출하는 것인데 호출하는 과정에서 상태나 props를 다시 읽어오는게 아닌건가요??

sonak22 commented 3 years ago

제가 이해하기로는... deps 배열 안에서 참조하지않으면 props가 변경되어도 함수가 새로 생성되지 않습니다. 함수가 새로 생성되지 않은 경우에는 함수 내의 props가 변경되더라도 최신 props 값이 사용되지 않고, 이전에 함수 생성시의 props가 사용되어집니다.

wenodev commented 3 years ago

useCallback

jody96 commented 3 years ago

감사합니다!!

sshusshu commented 3 years ago

useCallback : 특정 함수 재사용 useMemo: 특정 결과값 재사용

컴포넌트에서 props 가 바뀌지 않았으면 Virtual DOM 에 새로 렌더링하는 것 조차 하지 않고 컴포넌트의 결과물을 재사용 하는 최적화 작업을 하려면, ***함수 재사용 필수

deps 배열에 꼭 포함되야 하는 것: 함수 안에서 사용하는 상태 혹은 props

컴포넌트 렌더링 최적화 작업을 해주어야만 성능이 최적화 : useCallback & React.memo

xoxwgys56 commented 2 years ago

devtools 4를 사용하시는 분들은 component 탭을 찾아서 사용하시면 됩니다.
만약에 잘나오지 않는다면, 현재 활성화된 탭을 종료하고 다른 탭에서 실행하시면 됩니다.

hook이 나오지 않는다면, components 탭 내의 설정 옵션에서 components 탭 안의 Always parse hook names from source (may be slow)을 체크해주시면 됩니다.

123rudgks commented 2 years ago

리액트를 다루는 기술을 보다가 궁금한 점이 생겨서 이 글을 읽어보았는데 여전히 해결이 되질 않네요 useCallback은 useMemo에서 파생되었음을 설명하면서 아래 코드는 완전히 똑같은 코드임을 설명하는 예시였는데 useMemo의 첫 번째 파라미터에서 왜 굳이 함수 안에서 또 함수를 만들고 이를 return을 하는지 모르겠습니다.

useCallback( ()=> {
    console.log('hello world');
}, [])

useMemo( ()=>{
    const fn = ()=>{ console.log('hello world') };
    return fn;
},[])

제 생각에는 useMemo( ()=> {console.log("hello world")}, [] ) 이렇게 사용하거나 retrun fn을 fn()로 변경시켜주어도 문제가 없을 줄 알았는데 막상 실행해보면 ~~is not a function이라는 에러가 뜹니다. 꼭 함수를 return을 해주어야 하는데 이 부분이 왜 useCallback하고 다를까요?

Choyeongdeok commented 2 years ago

@123rudgks usememo의 첫 번째 파라미터는 일반 함수가 아닌 팩토리 함수를 넣어야 합니다. 팩토리 함수란 객체를 반환하는 함수입니다. useMemo의 목적이 특정 값을 재사용하는 것이므로 뭔가 반환되는 값이 있어야 정상적으로 작동할 것입니다.

() => { const fn = ()=>{ console.log('hello world') }; return fn; }

위 함수가 팩토리 함수라고 생각하면 될 것 같습니다

josunyjostar commented 2 years ago

고급지고 우아하다

ggsno commented 2 years ago

@123rudgks usememo는 첫번째 파라미터 함수의 '반환 값'을 사용한다고 보시면 될거같습니다. 예시에서 함수의 안에 함수를 만들어 반환하는 이유는 바깥 함수의 반환 값으로 안쪽 함수를 사용하기 위함입니다. 그냥 함수 하나를 전달하면 그 함수의 반환값을 사용합니다. 함수 자체를 사용하기 위해서는 해당 함수를 반환하는 함수를 만들어 사용해야 합니다.

serpiko-git commented 2 years ago

@123rudgks

useCallback 1 const onToggle = useCallback( 2 () => { 3 / ... / 4 }, [users] 5 );

useMemo 1 const onToggle = useMemo( 2 () => () => { 3 / ... / 4 }, [users] 5 );

minsuhan1 commented 1 year ago

@pyeonjaesik 리액트 컴포넌트가 리렌더링되면 해당 컴포넌트 함수(ex. function App { ... })가 새로 만들어집니다. 동시에 컴포넌트 안에 정의된 함수들도 새로 만들어집니다. 물론 함수의 내용(코드) 자체는 변하지 않습니다. 자바스크립트에서는 함수도 곧 객체이기 때문에 리렌더링이 되면 '내용이 동일한 새로운 함수 객체'가 생성된다고 알고 있습니다. 따라서 useCallback을 사용하면 deps 의 업데이트가 있지 않는 이상 함수객체를 독립된 메모리에 캐싱해두고, 리렌더링 시 새로운 함수객체를 생성하지 않도록 할 수 있습니다.

seho0808 commented 7 months ago

음... 저는 여기 있는 대부분의 의견과는 다른 생각을 가지고 있는데요, (+본문도 첫 문단부터 틀렸다고 생각합니다.)

useCallback이든 useMemo든 함수 자체는 매 리렌더마다 새로 생성합니다.

function Component() {
 //...
  const temp1 = useCallback(() => {console.log('a')}, [])
  const temp2 = useMemo(() => {console.log('a')}, [])
  //...
}

위 컴포넌트 리렌더 마다 temp1과 temp2가 실행될텐데요, deps가 어떻든 () => {} 콜백은 무조건 읽기는 하기 때문에 함수 생성은 항상 진행됩니다.

대신 temp1같은 경우에는 함수의 레퍼런스를 유지해주고 temp2는 함수의 결과를 유지해줍니다.

함수는 매번 재생성 하는게 이미 리소스를 많이 잡아먹는 것 같은데 레퍼런스 유지하는게 의미가 있나요? 라고 궁금하실텐데요,

함수를 1. 자식에게 전달하거나 함수를 2. useEffect의 dependency로 사용하는 등의 경우에서 도움이 됩니다. 레퍼런스를 유지하기에 자식 컴포넌트의 prop이 안바뀐 것으로 취급되거든요. useEffect dep도 마찬가지입니다. 예시들은 공식문서에서 여기 말씀드린 그대로 찾을 수 있습니다. (https://react.dev/reference/react/useCallback)