✍️ useEffect 정리
const Datail = ({ shoes }) => {
let [count, setCount] = useState(0);
useEffect(() => {
// 1)
console.log('안녕'); // 2)
});
// 3)
...
return (
...
{count}
<button
onClick={() => {
setCount(count + 1);
}}> 버튼 </button>
...
<React.StrictMode>
를 제거하면 한 번만 뜨게 할 수도 있다.❓🤔
2)
에서 해당 코드를useEffect
바깥3)
에서 실행해도 같은 결과가 나온다.
그럼 언제useEffect
를 쓰면 좋을까?
useEffect
안에 적은 코드는 렌더링이 되고 난 후 실행이된다는 점을 기억하자! 즉 html을 다 그려준 뒤 해당 코드가 실행된다.- 만약
3)
위치에 복잡한 연산이 필요한 코드가 작성되어 있다면 html 렌더링 작업은 딜레이가 생길 수 있지만,useEffect
안에 작성한다면 html이 먼저 로딩이 된 후 실행되므로 더 효율적으로 동작할 수 있다.😀
📌 따라서 아래와 같은 경우 useEffect
안에 코드를 작성하면 효율적으로 렌더링이 가능해진다.
✍️ Side Effect 란?
useEffect
로 이름이 지어졌다고 한다.🧐📌 과제 1) Detail 페이지 방문 후 2초 후에 alert 박스가 사라지게 만들기
const Datail = ({ shoes }) => {
let [alert, setAlert] = useState(true); // 1)
useEffect(() => {
console.log('useEffect 됐다!');
setTimeout(() => {
setAlert(false);
}, 2000);
});
return (
<div className='container'>
{alert === true ? // 2)
(<div className='alert alert-werning'>2초이내 구매시 할인</div>) : null}
...
state가 true 라면 alert 박스가 보이도록, false라면 안보이도록 구현해보자.
1) useState
로 초기 상태값을 true로 설정한다.
→ UI 상태 저장할 state 만들기
2) 삼항연산자를 통해서 조건을 설정한다.
→ state에 따라서 UI가 어떻게 보일지 작성하기
html이 먼저 렌더링된 후, useEffect
안의 setTimeout
을 통해서 2초 뒤 state를 false로 변경한다.
📌 solution
[]
빈 배열을 설정하는 것이 더 정확한 답이다. (mount에만 실행)deps
에 alert를 넣으면 alert라는 state가 변할 때만 실행된다. 즉, mount될 때와 alert state가 변할 때 실행된다.📌 과제 2) 인풋 값에 숫자가아닌 문자를 입력했을 때 경고창 보여주기
let [message, setMessage] = useState(true);
const changeHandler = (e) => {
const value = Number(e.target.value); // 1)
if (isNaN(value)) { // 2)
setMessage(false);
} else {
setMessage(true);
}
};
return (
...
{message === true ? null : (
<div
className='alert alert-werning'
style={{ backgroundColor: 'orange' }}
> 숫자만 입력하세요 </div>
)}
<input type='text' onChange={changeHandler} />
✍️ 굳이 useEffect 훅으로 구현을 안해도 되지만, 훅을 활용하려면 어떻게 사용해야할까? 🤔
useEffect(() => {
console.log('useEffect 됐다!');
let a = setTimeout(() => { // 1)
setAlert(false);
}, 2000);
return () => {
console.log('useEffect가 동작되기 전에 실행되는 코드');
clearTimeout(a); // 2)
};
}, [count]);
setTimeout
함수를 변수에 할당해서 사용하면 2) 에서 처럼 clearTimeout
함수를 통해서 타이머를 제거해줄 수 있다.서버에 GET 요청하기
GET 요청 방법
ajax 옵션 세가지
라이브러리 설치 및 셋팅
$ npm install axios
import axios from 'axios';
axios.get('url')
axios.get('url').then
catch
문 작성ex.)
<button
onClick={() => {
// axios 라이브러리 사용
axios
.get('https://codingapple1.github.io/shop/data2.json')
.then((result) => {
console.log(result.data); // 1)
})
.catch(() => {
console.log('실패했을 때 처리할 코드');
});
}}
>
데이터 받아오기
</button>
// fetch 사용예
fetch('https://codingapple1.github.io/shop/data2.json')
.then((res) => res.json()) // array/object 변환과정 필요
.then((data) => {
console.log(data); // 2)
});
Promise.all [ axios.get('/url1'), axios.get('/url2') ]
.then(()=>{}) // 전부 성공할경우 실행
📌 과제) 서버에 GET 요청해서 받아온 데이터 보여주기
<button
onClick={() => {
axios
.get('https://codingapple1.github.io/shop/data2.json')
.then((result) => {
const copyShoes = [...shoes, ...result.data];
setShoes(copyShoes);
})
.catch(() => {
console.log('실패했을 때 처리할 코드');
});
}}
>
데이터 받아오기
</button>
✍️ 첫 시도에는 map()
함수와 push()
를 사용했는데 이 방법보다는 복사해온 데이터에 바로 합칠 수 있는 concat
이나 혹은 spread
문법을 이용하는 것이 좋을 것 같다. 😀
서버에 POST 요청하기
서버로 데이터전송하는 POST 요청
axios.post('/uri주소', {name:'seul'})
// state 저장
let [tab, setTab] = useState(0);
// TabComponent를 보여줄 UI
<TabContent tab={tab} />
// state를 동작 시킬 스위치
<Nav.Link
eventKey='link0'
onClick={() => {
setTab(0);
}}
>
// TabContent 컴포넌트
const TabContent = ({ tab }) => {
if (tab === 0) {
return <div>내용0</div>;
} else if (tab === 1) {
return <div>내용1</div>;
} else if (tab === 2) {
return <div>내용2</div>;
}
};
const TabContent = ({ tab }) => {
return [<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab];
};
✍️ 배열안에 보여줄 내용들을 전부 넣고 [tab]
의 상태값에 따라서 배열 인덱스에 맞는 요소를 return 한다.
아마 리턴해야할 내용이 길다면 가독성이 더 떨어지지 않을까? 하지만 이 예제에서는 이렇게 리팩토링하는 편이 더 간편한 것 같다. 😀
/* css */
.start {
opacity: 0;
}
.end {
opacity: 1;
transition: opacity 0.5s;
}
// TabContent component
const TabContent = ({ tab }) => {
let [fade, setFade] = useState(''); // 1)
useEffect(() => { // 3)
setTimeout(() => {
setFade('end'); // 3-2)
}, 100);
return () => {
setFade(''); // 3-1)
};
}, [tab]);
return (
<div className={`start ${fade}`}> // 2)
{[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]}
</div>
);
};
❓왜 그럴까? 🤔
최신 리액트의 automatic batching 기능 때문이라고 한다.
state 변경함수를 쓸 때마다 재렌더링을 하는 것이 아니라, state변경이 다 되고나서 재렌더링을 한번 시켜주기 때문!
따라서 3-1)
return(cleanup)문 에서 먼저 실행 후 3)-2
setFade 가 작동하도록 코드를 짜 두었어도 두 개를 합쳐서 한 번 렌더링을 실행하기 때문이다.
아래와 같이 useEffect내 cleanup기능을 사용해서 setTimeout을 활용할 수도 있다.
const TabContent = ({ tab }) => {
let [fade, setFade] = useState('');
useEffect(() => {
let fadeTimeout = setTimeout(() => { // 변수에 담고
setFade('end');
}, 100);
return () => {
clearTimeout(fadeTimeout); // 지워준다
setFade('');
};
}, [tab]);