takeEvery, takeLatest
99%의 경우 Whilte(true) 를 뺄 이유가 없다.
항상 반복이 되어야 하는 동작이다.
ex) 한번만 로그인하고 한번만 로그아웃 하지는 않음.
다른 유저가 접속했을 때 로그인후 로그아웃하고 다시 로그인 로그아웃을 해야하기 때문에
한번만 실행되는 액션은 없음!
takeEvery
그래서 takeEvery라는 것을 지원함.
기존:
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
fucntion* watchHello() {
while(true) {
// 액션을 테이크하는 부분
yield take(HELLO_SAGA);
// 실제 동작하는 부분 같이 있는 것이 문제
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
}
}
변경 후:
takeEvery와 제너레이터함수 function*()안에 넣기
=> HELLO_SAGA가 액션이 take되면 이후 실행되는 코드 부분이 구분이 됨!
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
function* watchHello() {
yield takeEvery(HELLO_SAGA, function*(){
console.log(1);
console.log(2);
console.log(3);
console.log(4);
});
}
HELLO_SAGA를 6번 dispatch하면, 실행부가 6번 반복됨.
또다른 예제)
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
function* watchHello() {
yield takeEvery(HELLO_SAGA, function*(){
yield put({
type: 'BYE_SAGA'
});
});
}
takeLatest
겉보기에는 takeEvery랑 비슷함.
근데 아래처럼 yield delay(1000); 비동기 코드가 들어갔다면
아래 코드가 takeEvery이면 dispatch 6번 => HELLO_SAGA 6번 / BYE_SAGA 6번 실행됨
takeLastest로 dispatch 6번 => HELLO_SAGA 6번 / BYE_SAGA 는 1번 실행됨
ex) 동시에 요청하면 마지막 것만 받겠다. 예를 들면 로그인 10번 클릭하면, 마지막 1번만 가도록함
takeLastest: 이전 요청이 끝나지 않은게 있다면 이전 요청을 취소합니다.
saga가 effect로 제너레이트를 이용해 제어해주는 것이다!
takeEvery takeLastest 무엇을 그럼 해야할까요?
=> 2번 동시에 실행했을 때 두 번 다 유효하게 해줄 것인가, 아니면 마지막것만 유효하게 해줄것인가? 로 판단하면 됨!
=> ex. 로그인 ( takeLastest) / 버튼 여러번 눌러서 카운트 올리는 거다 (takeEvery)
=> 여러번 클릭하는게 실수다! 그럼 takeLastest / 여러번 클릭하는게 다 동작해야한다 takeEvery
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
function* watchHello() {
yield takeLastest(HELLO_SAGA, function*(){
yield delay(1000);
yield put({
type: 'BYE_SAGA'
});
});
}
takeLastest의 경우 결과값 :
액션 분리!
전
function* watchHello() {
yield takeLastest(HELLO_SAGA, function*(){
yield delay(1000);
yield put({
type: 'BYE_SAGA'
});
});
}
후
function* hello() {
yield delay(1000);
yield put({
type: 'BYE_SAGA'
});
});
}
function* watchHello() {
yield takeLastest(HELLO_SAGA, hello);
}
fork, call, 사가 총정리
- fork call 둘 다 함수를 실행해주는 것임
- 사가에서는 함수실행도 effect로 많이 처리함
fork: 비동기처리 => 서버에 응답이 오는 말든 바로 다음 것 실행
call: 동기처리 => 서버 요청에서 응답이 와야 다음 것 실행
fork
사용전
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
export default function* userSaga() {
yield all([
watchLogin(),
watchHello(),
]);
}
사용후
all 둘다 watch하고 싶을 때 사용
fork를 붙이는 이유: watchLogin / watchHello는 순서가 없음.
순서가 없기 때문에 비동기로 처리해도 됨~
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
export default function* userSaga() {
yield all([
fork(watchLogin),
fork(watchHello),
]);
}
call
call: 동기처리 => 서버 요청에서 응답이 와야 다음 것 실행
순서를 지켜서 실행해야하는 것은 무조건 call!
사용전
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
function loginAPI() {
//서버에 요청을 보내는 부분
}
function* login() {
try{
yield delay(100);
yield put({
type: LOG_IN_SUCCESS,
});
} catch (e) {
console.error(e);
yield put({
type: LOG_IN_FAILURE,
});
}
}
사용후
call => loginAPI 요청이 끝나야 다음으로 실행됨.
yield call(loginAPI) 코드 실행이 에러가나면 바로 catch함수로 넘어감.
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
function loginAPI() {
//서버에 요청을 보내는 부분
}
function* login() {
try{
yield call(loginAPI);
yield put({
type: LOG_IN_SUCCESS,
});
} catch (e) {
console.error(e);
yield put({
type: LOG_IN_FAILURE,
});
}
}
또 다른 예제
call로 logger를 하면, 10초 기다린 후에 call(logInAPI)가 실행됨.
근데 fork를 하면 비동기로 완료되든 10초 걸리든 말든~ 알아서 실행해~
그러고 바로
call(logInAPI)가 실행되도록 한다.
=> 이 다음 성공하면 LOG_IN_SUCCESS
=> 실패하면 LOG_IN_FAILURE 로 넘어간다.
import { all, fork, takeLastest, takeEvery, call, put, take, delay } from 'redux=saga/effects';
function loginAPI() {
//서버에 요청을 보내는 부분
}
function* login() {
try{
yield fork(logger); // logger는 내 기록을 로깅하는 함수 10초걸림
yield call(loginAPI);
yield put({
type: LOG_IN_SUCCESS,
});
} catch (e) {
console.error(e);
yield put({
type: LOG_IN_FAILURE,
});
}
}
Redux-Saga
나는 로그인 하면서 서버에 요청도 보내고, 서버의 요청 결과도 다시 액션으로 받고싶다!
그런 경우에 redux만으로 안되고, 1)비동기나 2)타이머나 3)액션을 연달아서 실행할 수 있게 해주는 redux-saga를 씀
=> 이때 generator함수를 씀. yield라는 중단점만 잘 붙여주면된다!
사가패턴
비동기요청은 아래와 같은 request / success / failure 로 네이밍됨
saga를 쓰는구나~
동기 요청은
그냥 바로 아래처럼 동기이면 Redux만 사용하면 됨
보통 Request 할 때 로딩창 돌아가는 것 isLoading: True
성공하면 isLoading: false로 처리
+ try catch는 에러가나도 안죽도록 보호하는 것
yield call(loginAPI)가 성공할 수도 있고 실패할 수 있다! 실패할 경우 catch에 걸림.
그래서 redux-saga 패턴은?
saga₩user.js
1. 등록을 해놓고
2. takeEvery 인지 takeLatest인지 결정하고
3. 실제로 동작할 것은 아래처럼 적어주기
Q.
동기 / 비동기를 꼭 saga를 써서 제어해야하나요?
컴포넌트 자체에서 async와 await을 주어도 비동기처리를 할 수 있지 않나요?
A.
사가를 쓰면 컴포넌트에서 dispatch할 때 계속 비동기 처리를 중복적인 코드를 써서 처리해줘야하는데.
사가를 사용하면 컴포넌트에서 Dispatch만 해줘도 알아서 비동기 처리가 된다.
비동기를 관리하는 하나의 시스템으로 saga를 사용하는 것이다.
사가 쓰기 전 코드:
사가를 쓴 후 코드:
'[React] Front-End' 카테고리의 다른 글
[바닐라자바스크립트] Front-End 기본기 공부 LIST (0) | 2021.09.02 |
---|---|
Redux-saga [yield select]: Reducer 상태에서 데이터 가져와서 바로 사용하기 (0) | 2021.07.29 |
[FE] 이상적인 컴포넌트란? (0) | 2021.07.21 |
[React] 버그 수정: onChange 함수 실행했다가 reRender 너무 많다고 뜰 때 (1) | 2021.07.20 |
[FE] Redux (4): Redux-Saga 의 필요성 / 사용법 (조현영 Redux vs Mobx 복습) (0) | 2021.07.18 |