[React] 9. react-hooks(useMemo, useCallback, useContext)

dev·2020년 10월 20일
0

React

목록 보기
9/21

이어서 hooks API를 보도록 하겠습니다.

4. useMemo

useMemo는 하나의 상태값을 변경하였을 때 해당 컴포넌트가 다시 랜더링이 될때 불필요한 작업들을 하는경우가 발생합니다.
전에 했던 Hooks를 사용한 예제를 통해 설명드리겠습니다.

Hedaer.jsx
count 함수에 log가 찍히게 작성해보겠습니다.

import React, { useContext, useMemo } from "react";
import { TodoContext } from "./App.js";
const Header = ({ state }) => {
  const count = (state) => {
    console.log("Header 랜더링~"); //추가
    let cnt = state.filter((v) => v.status === "re").length;
    return cnt;
  };
  return (
    <div>
      <h1>Hooks를 사용한 예제</h1>
      <div>갯 수 : {count(state)}개</div>
    </div>
  );
};
export default Header;

실행하여 text입력란에 입력을 할경우
이렇게 Header가 랜더링 되는 것을 알 수 있습니다.
현재 change이벤트가 발생하여 setNewTodo를 하여 리랜더링이 발생을 하였는데 해당 Header는 불필요한 랜더링이 발생하는 걸 볼 수 있습니다.
Header에는 newTodo를 사용하지않고 state를 사용하는데 랜더링이 다시 될필요는 없던거죠.
그래서 이러한 불필요한 랜더링으로 앱의 성능을 저하시킬 수 있는걸 방지하기 위해 useMemo를 사용합니다.
useMemo에 해당 state를 저장하여 리랜더링 되었을 때 저장된 값과 비교하여 값이 동일하면 리랜더링이 되지 않게 해줍니다.
이제 코드를 변경해보겠습니다.

import React, { useMemo } from "react";
import { TodoContext } from "./App.js";
const Header = ({ state }) => {
  const count = (state) => {
    console.log("Header 랜더링~");
    let cnt = state.filter((v) => v.status === "re").length;
    return cnt;
  };
  const memoCount = useMemo(() => count(state), [state]);
  return (
    <div>
      <h1>Hooks를 사용한 예제</h1>
      <div>갯 수 : {memoCount}개</div>
    </div>
  );
};
export default Header;

작성 후 아까와 동일하게 작업을 했을 때 로그가 발생하지 않는 걸 확인 하실 수 있습니다.

5. useCallback

useCallback 은 useMemo와 상당히 비슷한 함수입니다. 주로 랜더링 성능을 최적화해야 하는 상황에서 사용하는데요, 이 Hook을 사용하면 이벤트 핸들러 함수를 필요할 때만 생성 할 수 있습니다.
컴포넌트가 리렌더링 될 때마다 함수들이 새로 생성됩니다. 대부분의 경우에는 이러한 방식이 문제가 되지 않지만, 컴포넌트의 렌더링이 자주 발생하거나, 렌더링 해야 할 컴포넌트의 개수가 많아진다면 이 부분을 최적화 해주시는 것이 좋습니다.

App.js에 적용시켜보도록 하겠습니다.

import './App.css';
import List from './List.jsx';
import React, { Component, useState, useEffect, useCallback } from 'react';
import Header from './Header.jsx';
import Form from './Form.jsx';
const App = () => {
  //useState를 사용하여 state 생성, 초기값 지정
  const [state, setState] = useState([
    {"id" :"js공부", "status" : "re"},
    {"id" :"react", "status" : "re"}]);   
  const[newTodo, setNewTodo] = useState();//useState를 사용하여 newTodo 생성 초기값 없음
  const changeInputData = useCallback((e) =>{ // onChange 이벤트 발생 시 NewTodo에 해당 값 저장
    setNewTodo(e.target.value);
  });
  const addTodo = useCallback((e) => {  // onClick 이벤트 발생 시 state에 해당 값 저장
    console.log("랜더링~~");
    e.preventDefault();
    setState([...state, {id : newTodo, status : 're'}]);
  });
  const changeT = useCallback((id) => { // 할 일 목록 내 item을 클릭 시 발생하는 이벤트 클릭 시 해당 값에 변화를 주기 위함
    const updateT = state.map(todo =>{
      if(todo.id === id){
        if(todo.status === 'done') todo.status= 're';
        else todo.status = 'done';
      }
      else{
      }
      return todo; 
    });
    setState(updateT);
 });
   useEffect( () =>{  //랜더링 될 때 호출
    console.log('새로운 내용이 랜더링됐네요', state);
   }, [state]); // 특정값 state에 지정하였기 때문에 newTodo가 변경될때는 타지 않음
  return (
    <>
      <Header state = {state}></Header>
      <form action="">
        <input type="text" name="" onChange={changeInputData} />
        <button onClick={addTodo}>할일추가</button>
      </form>
      <List todos= {state} changeT = {changeT}></List>
    </>
  )
}
export default App;

6. useContext

이 useContext을 사용하면 함수형 컴포넌트에서 Context 를 보다 더 쉽게 사용 할 수 있습니다.
useContext는 React.createContext로 context를 생성하여줍니다.

export const TodoContext = React.createContext();

그리고 상위 컴포넌트에서 Provider로 하위 컴포넌트에게 값을 넘겨 줄 수 있습니다.

<TodoContext.Provider value= {{state, changeInputData, addTodo}}>
</TodoContext.Provider

이 방법을 적용 시켜 보도록하겠습니다.
App.js

import './App.css';
import List from './List.jsx';
import React, { Component, useState, useEffect, useCallback } from 'react';
import Header from './Header.jsx';
import Form from './Form.jsx';
export const TodoContext = React.createContext();// useContext 생성
const App = () => {
  const [state, setState] = useState([
    {"id" :"js공부", "status" : "re"},
    {"id" :"react", "status" : "re"}]); 
  const[newTodo, setNewTodo] = useState();//useState를 사용하여 newTodo 생성 초기값 없음
  const changeInputData = useCallback((e) =>{ // onChange 이벤트 발생 시 NewTodo에 해당 값 저장
    setNewTodo(e.target.value);
  });
  const addTodo = useCallback((e) => {  // onClick 이벤트 발생 시 state에 해당 값 저장
    console.log("랜더링~~");
    e.preventDefault();
    setState([...state, {id : newTodo, status : 're'}]);
  });
  const changeT = useCallback((id) => { // 할 일 목록 내 item을 클릭 시 발생하는 이벤트 클릭 시 해당 값에 변화를 주기 위함
    const updateT = state.map(todo =>{
      if(todo.id === id){
        if(todo.status === 'done') todo.status= 're';
        else todo.status = 'done';
      }
      else{
      }
      return todo; 
    });
    setState(updateT);
 });
   useEffect( () =>{  //랜더링 될 때 호출
    console.log('새로운 내용이 랜더링됐네요', state);
   }, [state]); // 특정값 state에 지정하였기 때문에 newTodo가 변경될때는 타지 않음
  return (
    <TodoContext.Provider value= {{state}}>
      <Header></Header>
      <form action="">
        <input type="text" name="" onChange={changeInputData} />
        <button onClick={addTodo}>할일추가</button>
      </form>
      <List todos= {state} changeT = {changeT}></List>
    </TodoContext.Provider>
  )
}
export default App;

변경된 부분은
1. export const TodoContext = React.createContext();// useContext 생성
2. return에 최상위 태그 <> 를 <TodoContext.Provider value= {{state}}> 변경
3. Header에 속성 state 제거

다음으로
Header.jsx

import React, { useMemo, useContext } from "react"; // useContext 추가
import { TodoContext } from "./App.js";
const Header = () => {
  const { state } = useContext(TodoContext); //useContext 사용
  const count = (state) => {
    console.log("Header 랜더링~");
    let cnt = state.filter((v) => v.status === "re").length;
    return cnt;
  };
  const memoCount = useMemo(() => count(state), [state]);
  return (
    <div>
      <h1>Hooks를 사용한 예제</h1>
      <div>갯 수 : {memoCount}개</div>
    </div>
  );
};
export default Header;

Header.jsx에서 useContext를 추가하여 state값을 useCountext(TodoContext)로 받아와서 사용합니다.
실행해보면 동일하게 실행이 될 것입니다.

다음 시간에는 hooks Reducer를 알아보겠습니다.
Reducer는 redux할 때 도움이 되는 부분이여서 보면 좋습니다.

profile
studying

0개의 댓글