what is custom Hooks?
- custom hooks도 결국 일반적인 함수의 하나이다.
- 단, 다른 함수들과 달리 블럭 안에 stateful logic을 포함할 수 있다. => hooks와 state를 사용할 수 있다.
- 이 때 어플리케이션에서 중복으로 사용되고 있는 로직을 커스텀 훅으로 만들면, 여러 곳에서 재사용할 수 있다.
- 로직은 공유하고, state는 따로 갖는다.
- src/hooks 폴더를 만들어서 관리한다.
- remind | rules of hooks
- 컴포넌트 함수/ 커스텀 훅이 아닌 함수 안에서는 리액트 훅을 사용할 수 없다.
- (컴포넌트/커스텀)함수의 top level(direct level)에서 리액트 훅을 호출해야 한다.
how to make customHooks?
// use-counter.js
const useCounter = (props) => {
const [counter, setCounter] = useState(0);
useEffect(()=>{
const timer = setInterval(() =>{
setCounter(prev => prev+1);
}, 10000)
// 디펜던시가 있을 때에는 clear fn이 없으면 interval 함수가 쌓이기 때문
return () => clearInterval(timer);
}, [])
return counter;
}
// App.js
import useCounter from '../hooks/use-counter'
function App() {
const counter = useCounter(); // returning state
return (
<>
<h1>forward : {counter}</h1>
<h1>backward : {counter}</h1>
</>
)
}
- 커스텀 훅을 저장하고 있는 변수 명은 반드시 use로 시작해야 한다.
- 이것은 리액트에게 해당 함수가 일반 함수가 아니라, 커스텀 훅이라는 것을 명시할 뿐만 아니라,
- 해당 함수에서 rules of hooks를 따르겠다고 약속하는 것이다.
- 다른 컴포넌트에서 커스텀 훅을 호출하면, 커스텀 훅에서 사용하고 있는 state, effect 등이 그 컴포넌트에 묶인다(tied).
- 여러 컴포넌트에서 같은 커스텀 훅을 호출하더라도, 모든 컴포넌트가 각자의 state를 갖는다. => re-useable
- return | useState()가 배열을 반환하는 것처럼, 커스텀 훅이 어떤 값을 반환할지 정할 수 있다.
- parameter | 커스텀 훅도 함수이기 때문에 당연히 인자를 받을 수 있다.
const useCounter = (counterFn) => { // params
const [counter, setCounter] = useState(0);
useEffect(()=>{
const timer = setInterval(() =>{
setCounter(counterFn); // (prev)=> prev + 1 또는 -1
}, 10000)
return () => clearInterval(timer);
}, [counterFn]) // effect가 외부 데이터를 사용하고 있으므로 dependencies 전달
return counter;
}
useHttp hook
post request option
{
method: 'POST',
body: JSON.stringify({ text: userInput }), // 사용자가 입력한 값을 json으로 변환하여 백엔드에 전달
headers: {
'Content-Type': 'application/json',
},
}
how to make useHttp?
http request에서 공통적으로 사용될 수 있는 로직에는 다음과 같은 것들이 있다.
- loading state
- fetch
- error state & handling (try - catch)
const useRequest = (props) => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const sendRequest = useCallback(async (config, transformData) => {
try {
setIsLoading(true);
const res = await fetch(config.url, {
method: config.method ? config.method : 'GET',
headers: config.headers ? config.headers : {},
body: config.body ? JSON.stringify(config.body) : null,
});
if (!res.ok) throw new Error('Request Error!');
const data = await res.json();
transformData(data);
} catch (error) {
setError(error.messages || 'something wrong');
}
setIsLoading(false);
}, []);
return { isLoading, error, sendRequest };
};
export default useRequest;
- sendRequest를 반환해서 컴포넌트 안에서도 이 함수를 사용할 수 있게 만든다.
- key로 접근하기 위해서 배열 대신 객체를 반환한다.
- useCallback( (config, transformData) => sendReq, [])
- sendReq가 외부 데이터를 사용하지 않기 때문에 === dependency가 없다.
const { isLoading, error, sendRequest } = useRequest();
const enterTaskHandler = (taskText) => {
const config = {
url: '백엔드 url',
method: 'POST',
body: { text: taskText },
headers: { 'Content-Type': 'application/json' },
};
const transformData = (data) => {
const generatedId = data.name;
const createdTask = { id: generatedId, text: taskText };
props.onAddTask(createdTask);
};
sendRequest(config, transformData);
};
+) fn.bind(fn의 this가 가리킬 값, fn이 실행될 때 인자로 사용될 값) | bind는 나중에 실행 될 fn을 준비(pre-configure)하는 역할을 한다.
- this가 가리킬 값을 null로 지정하면, fn이 원래 가리킬 this를 그대로 사용한다.
- ()=>{ greet('max') }와 greet.bind(null, 'Max')는 같다. 이 때 bind가 가리키는 this는 greet이다.
someButton.addEventListener('click', greet.bind(null, 'Max'));
'Study > React' 카테고리의 다른 글
Deployment | firebase hosting (0) | 2022.07.04 |
---|---|
Validation (0) | 2022.06.27 |
HTTP Request (0) | 2022.06.23 |
Optimization | how does react work? (0) | 2022.06.22 |
etc | Image, Icon, Input (0) | 2022.06.21 |