'리액트를 다루는 기술' 8장, Hooks

Jake_Young·2020년 8월 21일
0
post-thumbnail

😁 useState

import React, { useState } from "react";

export default function Counter() {
  const [value, setValue] = useState(0);

  return (
    <div>
      <h1>The number is {value}</h1>
      <button
        onClick={() => {
          setValue(value + 1);
        }}
      >
        +1
      </button>
      <button
        onClick={() => {
          setValue(value - 1);
        }}
      >
        -1
      </button>
    </div>
  );
}

😃 useEffect

  • 맨 처음에만 한번 실행되기를 원한다면 useEffect 2번째 인자에 빈 배열을 넣는다
  • 특정 값이 변결될 때만 호출하고 싶다면, 2번째 인자에 그 변수를 포함한 배열을 넣는다
  • props, state 모두 가능
  • useEffet는 기본적으로 렌더링되고 난 직후마다 실행된다.
  • 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면,
  • 뒷정리 함수(cleanup)를 반환해 주어야 한다.
  • useEffect 첫번째 인자의 반환값에 return ()=>{}이 cleanup 함수이다.
//Info.js
import React, { useState, useEffect } from "react";

export default function Info() {
  const [name, setName] = useState("");
  const [nickname, setNickname] = useState("");

  useEffect(() => {
    console.log("effect");
    console.log(name);
    return () => {
      console.log("cleanup");
      console.log(name);
    };
  });


  const onChangeName = (e) => {
    setName(e.target.value);
  };

  const onChangeNickname = (e) => {
    setNickname(e.target.value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={onChangeName} />
        <input value={nickname} onChange={onChangeNickname} />
      </div>
      <div>
        <b>name:</b> {name}
        <b>nickname:</b> {nickname}
      </div>
    </div>
  );
}
//App.js
import React, { useState } from "react";
import Info from "./Info";

import "./styles.css";

const App = () => {
  const [visible, setVisible] = useState(false);

  return (
    <div className="App">
      <h1>New App</h1>
      <h1>Please, Work For Me</h1>
      <button
        onClick={() => {
          setVisible(!visible);
        }}
      >
        {visible ? "hide" : "show"}
      </button>
      {visible && <Info />}
    </div>
  );
};

export default App;

🙄 useReducer

  • useReducer를 사용했을 때의 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것
  • useState와 거의 비슷하다.
  • 이후 Redux에서 같은 방식의 작업을 한다.
//ForReducer.js
import React, { useReducer } from "react";

function reducer(state, action) {
  // action.type에 따라 다른 작업 수행
  switch (action.type) {
    case "INCREMENT":
      return { value: state.value + 1 };
    case "DECREMENT":
      return { value: state.value - 1 };
    default:
      return state;
  }
}

export default function ForReducer() {
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  return (
    <div>
      <h1>The number is {state.value}</h1>
      <button
        onClick={() => {
          dispatch({ type: "INCREMENT" });
        }}
      >
        +1
      </button>
      <button
        onClick={() => {
          dispatch({ type: "DECREMENT" });
        }}
      >
        -1
      </button>
    </div>
  );
}
//ForReducer2.js
import React, { useReducer } from "react";

function reducer(state, action) {
  return {
    ...state,
    [action.name]: action.value
  };
}

export default function ForReducer2() {
  const [state, dispatch] = useReducer(reducer, {
    name: "",
    nickname: ""
  });

  const { name, nickname } = state;

  const onChange = (e) => {
    dispatch(e.target);
  };

  return (
    <div>
      <div>
        <input name="name" value={name} onChange={onChange} />
        <input name="nickname" value={nickname} onChange={onChange} />
      </div>
      <div>
        <b>name:</b> {name}
        <b>nickname:</b> {nickname}
      </div>
    </div>
  );
}

😏 useMemo

  • useMoemo를 사용하면 컴포넌트 내부에서 발생하는 연산을 최적화 할 수 있다.
  • 아래에는 기존의 useState를 사용한 평균값 계산 컴포넌트가 있다.
import React, { useState } from "react";

const getAverage = (numbers) => {
  console.log("calculating now");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");
  const onChange = (e) => {
    setNumber(e.target.value);
  };
  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number, 10));
    setList(nextList);
    setNumber("");
  };
  return (
    <div>
      <h1>Average</h1>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>Enroll</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b> {getAverage(list)}
      </div>
    </div>
  );
}
  • 이를 실행하면 우리가 숫자를 등록할 때뿐만 아니라 인풋 내용이 수정될 때에도 우리가 만든 getAverage 함수가 호출되는 것을 확인할 수 있다.
  • 특정 값이 바뀌었을 때만 연산이 실행될 수 있게 useMemo를 사용해보자.
import React, { useState, useMemo } from "react";

const getAverage = (numbers) => {
  console.log("calculating now");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = (e) => {
    setNumber(e.target.value);
  };

  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number, 10));
    setList(nextList);
    setNumber("");
  };

  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <h1>Average</h1>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>Enroll</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b> {avg}
      </div>
    </div>
  );
}

😙 useCallback

  • 이벤트 핸들러 함수를 필요할 때만 생성할 수 있다.
  • 방금 위 onChange와 onInsert 함수는 새로 렌더링 할 때마다 매번 새로 생긴다.
  • 이런 부분을 최적화 시켜줄 수 있다.
  • useCallback은 useMemo를 사용해 똑같이 구현할 수 있다.
  • 일반적으로 useMemo는 그 결괏값을 반환하는데 반해 useMemo로 함수를 반환한다면,
  • 이는 useCallback과 같은 기능을 하는 것이다
useCallback(()=>{
	console.log('hello World!')
},[])

useMemo(()=>{
  const fn = () => {
    console.log('hello World!')
  };
  return fn;
},[])
import React, { useState, useMemo, useCallback } from "react";

const getAverage = (numbers) => {
  console.log("calculating now");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = useCallback((e) => {
    setNumber(e.target.value);
  }, []);

  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number, 10));
      setList(nextList);
      setNumber("");
    },
    [number, list]
  );

  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <h1>Average</h1>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>Enroll</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b> {avg}
      </div>
    </div>
  );
}

😋 useRef

  • ref 기능을 사용하고자 할 때 쓸 수 있다.
  • 컴포넌트의 로컬 변수를 사용해야 할 때도 사용할 수 있다.
  • useRef의 객체는 렌더링에 영향을 미치지 않는다.
import React, {useRef} from "react"

const RefSample = () => {
  const id = useRef(1);
  const setId = (n) => {
    id.current = n;
  }
  const printId = () => {
    console.log(id.current)
  }
  return (
    <div>
      refsample
    </div>
  )
}

export default RefSample;

😜 Custom Hooks

  • 자유롭게 커스텀하여 써보자
//useInputs.js
import { useReducer } from "react";

function reducer(state, action) {
  return {
    ...state,
    [action.name]: action.value
  };
}

export default function useInputs(initialForm) {
  const [state, dispatch] = useReducer(reducer, initialForm);

  const onChange = (e) => {
    dispatch(e.target);
  };

  return [state, onChange];
}

//Info.js
import React from "react";
import useInputs from "./useInputs";

export default function Info() {
  const [state, onChange] = useInputs({
    name: "",
    nickname: ""
  });

  const { name, nickname } = state;

  return (
    <div>
      <div>
        <input value={name} onChange={onChange} />
        <input value={nickname} onChange={onChange} />
      </div>
      <div>
        <b>name:</b> {name}
        <b>nickname:</b> {nickname}
      </div>
    </div>
  );
}
profile
자바스크립트와 파이썬 그리고 컴퓨터와 네트워크

0개의 댓글