08.hooks
Updated:
1.React Hooks 이론
- ReactHooks 는 class 없이 state 를 사용할 수 있는 새로운 기능입니다.
Hooks 가 필요한 이유
항상 기술은 그 전에 것의 불편함이나 문제점을 해결하기 위해서 더욱 발전합나디ㅏ.
그와 같이 React Hooks 도 주로 Class Component 로 사용되어온 React 에서 느껴왔던 불편함이나 문제점들을 해결하기 위해서 개발 되었습니다.
원래는 React는 주로 Class Component 를 사용하고 React Hooks는 Functional component 를 사용합니다.
- React lifecycle
Class Component 에서는 생명주기를 이용할 때 componentDidMount, ComponentDidUpdate, componentWillUnmount 이렇게 다르게 처리를 해주지만 react hooks 를 사용할 때는 useEffect 안에서 다 처리를 해줄수 있기 때문입니다.
2.useState
-
state를 대체 해서 function component
-
useState 는 두번째 있는 setState 는 값이 변경 될때 마다 re-render 가 된다.
const [item, setItem] = useState(1);
위에 코드에서 useState 는 number 1 을 초기 값으로 하고 return 으로 arr 를 2가지 value 를 return 합니다.
첫번째는 item은 초기값인 1을 저장하는 값이고 setItem 은 변경을 위한 value 값이라고 보면 됩니다.
import React, { useState } from "react";
import "./App.css";
const App = () => {
const [item, setItem] = useState(1);
const incrementItem = () => setItem(item + 1);
const decrementItem = () => setItem(item - 1);
return (
<div className="App">
<h1>Hello {item}</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={incrementItem}>Increment</button>
<button onClick={decrementItem}>Decrement</button>
</div>
);
};
export default App;
3.useEffect
-
라이프 사이클 훅을 대체할 수 있다 (componentDidMount, componentDidUpdate, componentWillUmount)
-
2개의 parameter 를 받는데, 첫번째는 function 으로써의 effect, 두번째는 dependency 임
예시
// sayHello 라는 함수가 변할때 (render) 될때 마다 실행되는대 dependency 가 number 인 것만 의존적으로 변경된다는 의미임
// componentDidMount 와 componentWillUpdate 인데 number 일때만 업데이트를 함
useEffect(sayHello, [number]);
// 두번째 인자로 빈 arr 을 사용하게 되면 초기 실행 할때 1번만 실행하게 됨
useEffect(sayHello, []);
- 즉 useEffect 는 componentDidMount, componentWillUnMount, componentDidUpdate 을 functional component 에서 사용할 수 있게 해줌
4.custom Hooks
- 사용자의 편의에 의해서 hook 을 재작해서 사용하는 것
HOC 대체
- HOC component 를 Custom Hooks 로 대체해서 많은 Wrapper component 를 줄일 수 있게 됨
HOC (Higher Order Component)란?
화면에서 재사용 가능한 로직만을 분리해서 component로 만들고, 재사용 불가능한 UI 와 같은 다른 부분들은 parameter 로 받아서 처리하는 방법입니다.
- 예를 들어 유저 리스트를 가져오는 공통적인 부분을 HOC 로 만들어서 넣어주고, 그 HOC 를 각각 컴포넌트로 감싸주면 모든 컴포넌트가 따로 인증을 위한 부분은 넣지 않아서 중복된 코드들을 반복적으로 처리 할 수 있습니다. Hooks 가 나오기 전에 class component 에서는 HOC 를 주로 사용했습니다. 그러나, 문제는 코드가 복잡하고 길어지게 되면 많은 Wrapper component 가 생길 수 있다는 것입니다. Wrapper 가 너무 많아 지게 되면 데이터 흐름을 파악하기가 힘들어 집니다.
// Wrapper 예시
<LanguageHOC>
<ThemeHOC>
<AuthHOC>
<APage />
</AuthHOC>
</ThemeHOC>
</LanguageHOC>
이러한 문제들은 Hooks 의 Custom Hooks 를 사용해서 해결 할 수 있습니다.
useWindowWidth
// window 창이 resize 될때마다 width 값을 return하는 custom hook
import { useEffect, useState } from "react";
function useWindowWidth() {
// 현재 브라우저의 width 값의 초기화 값임
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
// resize 함수 선언
const resize = () => {
setWidth(window.innerWidth);
};
// window 에 resize 가 일어나면 resize 함수가 실행 되게 이벤트 설정
window.addEventListener("resize", resize);
// cleanUP
return () => {
window.removeEventListener("resize", resize);
};
// dependency 설정
}, []);
return width;
}
export default useWindowWidth;
5.useReducer
-
useState 의 확장판임
-
다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우
-
다음 state 가 이전 state 에 의존적인 경우
-
Redux 를 안다면 쉽게 사용 가능
// useReducer sample
import { useReducer } from "react";
// reducer - state 를 변경하는 로직이 담겨 있는 함수 이전 state 에 의존적일때 사용하기 좋음
const reducer = (state, action) => {
if (action.type === "PLUS") {
return {
count: state.count + 1,
};
}
return state;
};
// dispatch - action 객체를 넣어서 실행
// action 은 객체이고 필수 프로퍼티로서 type 을 가진다
const Example6 = () => {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>You clicked {state.count} times</p>
<button onClick={click}>Click me</button>
</div>
);
function click() {
dispatch({ type: "PLUS" });
}
};
export default Example6;
6.useMemo, useCallback, useRef
-
render 사이에서 어떠한 상태를 유지하기 위한 기능임
-
class component 를 사용할때는 계속 render method 만 돌기 때문에 class 안에 있는 것들이 유지가 되었는데, function 은 생성이 되면서 그안에 있는 것들은 새로 생성되는 경향이 있기때문에 render 사이에 어떠한 값을 저장하고 보관 하는 용도로 사용되는 hooks (useMemo, useCallback, useRef)라고 보면 됨
useMemo , useCallback
// useMemo, useCallback, useRef 사용하기
import { useCallback, useMemo, useState } from "react";
const sum = (persons) => {
console.log("sum...");
return persons.map((person) => person.age).reduce((a, b) => a + b, 0);
};
const Example7 = () => {
const [value, setValue] = useState();
const [persons] = useState([
{ name: "Jacob", age: 30 },
{ name: "John", age: 20 },
]);
// 반복된 사용을 막기 위해 useMemo 사용
// Persons 에 의존적인 count 는 다시 계산이 안되게 하는것 => react를 최적화 하는데 도움이 많이 됨
const count = useMemo(() => {
return sum(persons);
}, [persons]);
// useCallback 은 안에 들어있는 함수를 언제 새로 세팅해 줄건지를 dependency 리스트에 의존적으로 결정해서 클릭안으로 넣어 줄건지를 정해주는것
const click = useCallback(() => {
console.log(value);
}, []);
const change = (e) => {
setValue(e.target.value);
};
return (
<div>
<input value={value} onChange={change} />
<p>{count}</p>
<button onClick={click}>Click</button>
</div>
);
};
export default Example7;
useRef
// useRef 사용하기
import { createRef, useRef, useState } from "react";
const Example8 = () => {
const [value, setValue] = useState();
// createRef 로 만든 input1Ref 는 항상 유지하는것이 아니라 render 될때 마다 새로 reference 를 만들어서 render 가 될때 마다 input에 넣어주는것
// 즉, render 될때만 넣어주는것
const input1Ref = createRef();
// useRef 로 만든 input2Ref 는 render 를 돌아도 계속 유지를 하는것 대신에 첫번째는 진행된것이 없으니까 undefined 가 나온거고 render 가 진행되면 생성된 reference 값을 나타내게 됨
// render 사이에도 그것을 유지해주는 것
const input2Ref = useRef();
console.log(input1Ref.current, input2Ref.current);
const change = (e) => {
setValue(e.target.value);
};
return (
<div>
<input value={value} onChange={change} />
<input ref={input1Ref} />
<input ref={input2Ref} />
</div>
);
};
export default Example8;
7.React router hooks
useHistory
- useHistory는 일반적인 history 객체와 똑같은 객체를 가지므로 사용법이 동일하며, 사용하기 위한 준비단계는 거의 없습니다.
import React from "react";
import { useHistory } from "react-router-dom";
const Home = () => {
const history = useHistory();
// history를 props에서 얻어왔을 때 처럼 동일하게 사용 가능하다.
return (
<div onClick={() => history.push("/auth")}>
<div>Hello!</div>
</div>
);
};
export default Home;
useLocation
- useLocation hooks는 사용자가 현재 머물러있는 페이지에 대한 정보를 알려주는 hooks 입니다. 이 hooks는 defaultProps 하나인 location 객체를 대체 하는 react-router-dom hooks 입니다.
import React from "react";
import { useLocation } from "react-router-dom";
const Home = () => {
const location = useLocation();
console.log(location); // pathname과 search라는 객체가 출력됩니다.
return <></>;
};
export default Home;
- search는 쿼리스트링을 분석해야하는 상황에서 유용합니다. 앞에서 pathname이 출력했던 부분을 제외한 쿼리스트링이 출력되며, 결과는 ?keyword=리액트 라는 출력결과가 나옵니다. 출력된 값은 query-string이라는 쿼리스트링 파싱 라이브러리를 이용하여 제대로 된 값을 얻어낼 수 있습니다.
import React from "react";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
const Home = () => {
const { search } = useLocation();
// search: ?keyword=리액트
const { keyword } = queryString.parse(search);
// keyword 출력결과: "리액트"
return <></>;
};
export default Home;
useParams
-
useLocation 과 비슷한 hooks 인데 왜냐하면 useLocation은 쿼리스트링 정보를 얻는데 유용하다고 하면, useParams는 path parameter의 정보를 얻을 수 있는 hooks이기 때문입니다.
-
먼저, useParams를 사용하기 위해서 동적 라우팅 설정을 해주어야 합니다. 설정법은 App.js에서 다음과 같이 내용을 추가해주시면 됩니다.
// App.js
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "components/Home";
const App = (): JSX.Element => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/home/:id" component={Home} />
{/* id라는 동적 라우팅값을 걸어주었다. */}
</Switch>
</BrowserRouter>
);
};
export default App;
-
: (콜론)을 이용하여 다음과 같이 설계해놓으면, /home/ 뒤에 1이 오든, 2가 오든, 혹은 문자열이 오든 상관없이 라우팅이 정상 작동됩니다.
-
이제 Home 컴포넌트에서 path parameter ( 동적 라우팅 값 )을 읽어오도록 하겠습니다. 기존에는 match props를 이용하여 match.params.id의 형식으로 접근을 해야 했지만, useParams를 이용하면 좀 더 간단하게 접근 가능합니다.
// in home.js
import React from "react";
import { useParams } from "react-router-dom";
const Home = () => {
const { id } = useParams();
// 동적 라우팅 값으로 걸어둔 이름으로 객체를 가져올 수 있다.
// 현재 주소의 값이 http://localhost:3000/home/3 일때
console.log(id); // "3"이 출력된다.
return <></>;
};
export default Home;
🔶 🔷 📌 🔑
Reference
-
react hooks - https://reactjs.org/docs/hooks-intro.html
-
react Ajax- https://reactjs.org/docs/faq-ajax.html
Leave a comment