[React] React Hooks - Custom Hooks

Hannahhhยท2022๋…„ 9์›” 29์ผ
0

React

๋ชฉ๋ก ๋ณด๊ธฐ
26/30

๐Ÿ” Custom Hooks


๊ฐœ๋ฐœ์ž๊ฐ€ ์Šค์Šค๋กœ ์ปค์Šคํ…€ํ•œ Hook์„ ์˜๋ฏธํ•˜๋ฉฐ, Custom Hook์„ ์ด์šฉํ•ด ๋ฐ˜๋ณต๋˜๋Š” ๋กœ์ง์„ ํ•จ์ˆ˜๋กœ ๋ฝ‘์•„๋‚ด์–ด ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฃผ๋กœ, ์—ฌ๋Ÿฌ url์„ fetchํ•  ๋•Œ, ์—ฌ๋Ÿฌ input์— ์˜ํ•œ ์ƒํƒœ ๋ณ€๊ฒฝ ๋“ฑ ๋ฐ˜๋ณต๋˜๋Š” ๋กœ์ง์„ ๋™์ผํ•œ ํ•จ์ˆ˜์—์„œ ์ž‘๋™ํ•˜๊ฒŒ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.


์ด๋•Œ, Custom Hook์„ ์ด์šฉํ•œ๋‹ค๋ฉด ์•„๋ž˜์˜ ์žฅ์ ์ด ์žˆ๋‹ค.

  • ์ƒํƒœ๊ด€๋ฆฌ ๋กœ์ง ์žฌํ™œ์šฉ ๊ฐ€๋Šฅ
  • Class Component๋ณด๋‹ค ์ ์€ ์–‘์˜ ์ฝ”๋“œ๋กœ ๋™์ผํ•œ ๋กœ์ง ๊ตฌํ˜„ ๊ฐ€๋Šฅ
  • ํ•จ์ˆ˜ํ˜•์œผ๋กœ ์ž‘์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ช…๋ฃŒํ•จ.



๐Ÿ‘€ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•(์˜ˆ์‹œ)


//FriendStatus : ์นœ๊ตฌ๊ฐ€ online์ธ์ง€ offline์ธ์ง€ returnํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ
function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

//FriendListItem : ์นœ๊ตฌ๊ฐ€ online์ผ ๋•Œ ์ดˆ๋ก์ƒ‰์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ
function FriendListItem(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

์œ„์— ์˜ˆ์‹œ์—์„œ FriendStatus ์ปดํฌ๋„ŒํŠธ๋Š” ์‚ฌ์šฉ์ž๋“ค์ด ์˜จ๋ผ์ธ์ธ์ง€ ์˜คํ”„๋ผ์ธ์ธ์ง€ ํ™•์ธํ•˜๊ณ , FriendListItem ์ปดํฌ๋„ŒํŠธ๋Š” ์‚ฌ์šฉ์ž๋“ค์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ์˜จ๋ผ์ธ์ด๋ผ๋ฉด ์ดˆ๋ก์ƒ‰์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

์ด ๋‘ ์ปดํฌ๋„ŒํŠธ๋Š” ์ •ํ™•ํ•˜๊ฒŒ ๋˜‘๊ฐ™์ด ์“ฐ์ด๋Š” ๋กœ์ง์ด ์กด์žฌํ•˜๊ณ  ์žˆ๋Š”๋ฐ,์ด ๋กœ์ง์„ ๋นผ๋‚ด์„œ ๋‘ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•ด Custom Hook์„ ์‚ฌ์šฉํ•ด๋ณด์ž.



1. Custom Hook ์ถ”์ถœ/์ •์˜


๋จผ์ €, ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๋กœ์ง์„ ๋ถ„๋ฆฌํ•œ๋‹ค.

์ด๋•Œ, Custom Hook์„ ์ •์˜ํ•˜๋Š” ๋ฐ ์•„๋ž˜์™€ ๊ฐ™์€ ์ผ์ข…์˜ ๊ทœ์น™์ด ์กด์žฌํ•œ๋‹ค.

  • Custom Hook์€ use๋กœ ์‹œ์ž‘ํ•˜๋Š” JavaScript ํ•จ์ˆ˜๋กœ, ๋‹ค๋ฅธ Hook(ex.useState)์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.(์ผ๋ฐ˜ ํ•จ์ˆ˜์—์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅ)

  • ํ”„๋กœ์ ํŠธ ๋‚ด์˜ Hooks ๋””๋ ‰ํ† ๋ฆฌ์— Custom Hook์„ ์œ„์น˜์‹œํ‚จ๋‹ค.

  • ํ•จ์ˆ˜๋Š” ์กฐ๊ฑด๋ถ€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ์–ด์•ผ ํ•œ๋‹ค.
    ์ฆ‰, return ๊ฐ’์ด ์กฐ๊ฑด๋ถ€๊ฐ€ ์•„๋‹ˆ์–ด์•ผ ํ•œ๋‹ค.


์˜ˆ์‹œ์—์„œ๋Š” Custom Hook์˜ ์ด๋ฆ„์„ useFriendStatus ๋กœ ์„ค์ •ํ–ˆ๋‹ค.

function useFriendStatus(friendID) {// useFriendStatus
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}



2. Custom Hook ์ด์šฉ


์œ„์—์„œ ์ถ”์ถœํ•œ Custom Hook์„ ์ ์šฉํ•˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

๊ธฐ์กด์˜ ์ฝ”๋“œ์™€ ๋น„๊ตํ–ˆ์„ ๋•Œ, ๋‘ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋” ์ง๊ด€์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹จ, ๊ฐ™์€ Custom Hook์„ ์‚ฌ์šฉํ–ˆ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ๋‘ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฐ™์€ ๋กœ์ง๋งŒ ๊ณต์œ ํ•  ๋ฟ, state๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๊ฐ™์€ state๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.




โœ” ์˜ˆ์‹œ2


const useFetch = ( initialUrl:string ) => {
	const [url, setUrl] = useState(initialUrl);
	const [value, setValue] = useState('');

	const fetchData = () => axios.get(url).then(({data}) => setValue(data));	

	useEffect(() => {
		fetchData();
	},[url]);

	return [value];
};

export default useFetch;

[์ฝ”๋“œ] ์—ฌ๋Ÿฌ url์„ fetchํ•  ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋Š” useFetch Hook


import { useState, useCallback } from 'react';

function useInputs(initialForm) {
  const [form, setForm] = useState(initialForm);
  // change
  const onChange = useCallback(e => {
    const { name, value } = e.target;
    setForm(form => ({ ...form, [name]: value }));
  }, []);
  const reset = useCallback(() => setForm(initialForm), [initialForm]);
  return [form, onChange, reset];
}

export default useInputs;

[์ฝ”๋“œ] ์—ฌ๋Ÿฌ input์— ์˜ํ•œ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ํ•  ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋Š” useInputs Hooks




๐Ÿ”ฅ ์‹ค์Šต ์˜ˆ์‹œ


โœ” custom hook์„ ์ด์šฉํ•˜์—ฌ useEffect ๋กœ์ง ๋ถ„๋ฆฌํ•˜๊ธฐ


// App.js

import useFetch from "./util/useFetch";
import "./styles.css";

//useFetch, useInput, useForm : custom hook
export default function App() {
  const fetchData = useFetch("data.json");

  return (
    <div className="App">
      <h1>To do List</h1>
      <div className="todo-list">
        {fetchData &&
          fetchData.todo.map((el) => {
            return <li key={el.id}>{el.todo}</li>;
          })}
      </div>
    </div>
  );
}
// .util/useFetch.js

import { useEffect, useState } from "react";

const useFetch = (fetchUrl) => {
  const [data, setData] = useState();

  useEffect(() => {
    fetch(fetchUrl, {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json"
      }
    })
      .then((response) => {
        return response.json();
      })
      .then((myJson) => {
        setData(myJson);
      })
      .catch((error) => {
        console.log(error);
      });
  }, [fetchUrl]);

  //loading, error
  return data;
};

export default useFetch;


[๊ทธ๋ฆผ]ํŒŒ์ผ list



[๊ทธ๋ฆผ]๊ฒฐ๊ณผ ํ™”๋ฉด



โœ” custom hook์„ ์ด์šฉํ•˜์—ฌ useEffect ๋กœ์ง ๋ถ„๋ฆฌํ•˜๊ธฐ


// App.js

import { useState } from "react";
import useInput from "./util/useInput";
import Input from "./component/Input";
import "./styles.css";

export default function App() {
  
  const [fistValue, firstBind, firstReset] = useInput("");
  const [lastValue, lastBind, lastReset] = useInput("");
  const [nameArr, setNameArr] = useState([]);

  const handleSubmit = (e) => {
    e.preventDefault();
    setNameArr([...nameArr, `${fistValue} ${lastValue}`]);
    firstReset();
    lastReset();
  };

  return (
    <div className="App">
      <h1>Name List</h1>
      <div className="name-form">
        <form onSubmit={handleSubmit}>
          <Input labelText={"์„ฑ"} value={firstBind} />
          <Input labelText={"์ด๋ฆ„"} value={lastBind} />
          <button>์ œ์ถœ</button>
        </form>
      </div>
      <div className="name-list-wrap">
        <div className="name-list">
          {nameArr.map((el, idx) => {
            return <p key={idx}>{el}</p>;
          })}
        </div>
      </div>
    </div>
  );
}
// .component/Input.js

function Input({ labelText, value }) {
  return (
    <div className="name-input">
      <label>{labelText}</label>
      <input {...value} type="text" />
    </div>
  );
}

export default Input;
// .util/useInput.js

import { useState } from "react";

function useInput(initialValue) {
  const [value, setValue] = useState(initialValue);

  const bind = {
    value,
    onChange: (e) => {
      setValue(e.target.value);
    }
  };

  const reset = () => {
    setValue(initialValue);
  };
  
//return ํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์€ ๋ฐฐ์—ด ํ˜•ํƒœ์˜ ๊ฐ’
  return [value, bind, reset];
}

export default useInput;


[๊ทธ๋ฆผ]ํŒŒ์ผ list



[๊ทธ๋ฆผ]๊ฒฐ๊ณผ ํ™”๋ฉด




Reference:

์ฝ”๋“œ์Šคํ…Œ์ด์ธ 

https://ko.reactjs.org/docs/hooks-custom.html

0๊ฐœ์˜ ๋Œ“๊ธ€