참고블로그
https://jeonghwan-kim.github.io/dev/2019/07/22/react-saga-ts-1.html
1. 비동기와 동기의 차이
https://private.tistory.com/24
동기: 동시에 일어난다.
비동기: 요청과 결과가 동시에 일어나지 않는다.
2. REST API
https://private.tistory.com/28?category=655782
3. redux saga
3-1. 왜 쓰는가
ㅁ
await fetch
setState
이런건 사실 한 스크린에서 써도 됨.
이거를 왜 미들웨어를 이용해서 써야하는가.
=> setState만 쓸거면 안쓰는게 맞고, Redux 안거치면 안써도 되잖아.
=> API 요청, 비동기 작업과 관련된 상태 관리를 위해 쓴다.
=> 로그인 유저정보 받아오는거 redux?
그리고 왜 thunk보다 saga를 써야하는가.
아니면 이런거는 setState니까 굳이 할 필요가 없는건가??
redux에 대해서만 이렇게 하고 한 스크린에서 괜찮은건 여기서 해도 되는건가?
- setState는 왜 비동기로 처리되는가
https://soldonii.tistory.com/112
앞선 글에서도 살펴봤지만, 리액트는 state가 변경될 때마다 매번 DOM을 re-rendering하는 것은 아니다. 퍼포먼스 효율성을 위해서 여러 차례 setState가 있을 경우 다른 state의 변경까지 한꺼번에 통합해서 리액트 자신이 판단하기에 가장 적절한 시기에 DOM을 re-rendering한다.
const [count, setCount] = useState(1);
setCount(prev => prev + 1) 이게 맞는 표현.
https://velog.io/@eomttt/Redux-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-Thunk-Saga
https://brunch.co.kr/@hee072794/108
이거 읽다가 mobx 잠깐 영업 당했음... 그래도 saga는 알아두면 좋으니 다시 이어서 공부해보자
- 여전한 나의 의문.
-- 그니까..
그냥 한 스크린에서 await, setState 해도 되지 않냐 이거지.
만약 redux를 쓴다해도 그냥 스크린 안에서
await, dispatch 하면 되는 문제 아냐?
dispatch를 하는김에 await도 같이 하면 되는건가......
그니까 왜 그걸 굳이 미들웨어까지 써서 하냐는거지...
그냥 함수 만들면 될것을...
이해가 안됨.
redux-thunk를 왜쓰냐.
간단하게 정리하면 redux-thunk는 일반 액션 생성자에 날개를 달아줍니다.
보통의 액션생성자는 그냥 하나의 액션객체를 생성할 뿐이지만 redux-thunk를 통해 만들게 되면
그 내부에서 여러가지 작업을 할 수도 있습니다.
https://jw910911.tistory.com/48
나랑 같은 생각을 가진 사람의 글 발견
https://changhoi.github.io/posts/etc/redux-async-demo/
https://github.com/reduxjs/redux-thunk/blob/master/src/index.js
redux-thunk를 위해 추가된 코드는 단 14줄.
https://react.vlpt.us/redux-middleware/04-redux-thunk.html
=> saga 볼 때도 이 글 보기.
store에 listener라는 기능도 있나보네? subscribe
React.FC<IProps>
그러면... 비동기 작업 + 리덕스 상태 변경을 위해 있는건가?
한 파일에서 코드가 길어지는걸 막는건가?
그러니까 한파일에서 미들웨어 없이 그냥 해도 되는건데, 그걸 미들웨어가 비동기 처리가 가능하기도 하고,
dispatch, getState를 쉽게 받아올 수 있기도 해서 쓰는거야?
나중에 맞는지 확인하기...
그래서 사실 한 스크린에서만 쓰는거면 await, setState도 좋은 방법인거지.
만약에 비동기 관련해서 loading, data, error 같은 데이터 흐름을 처리하고 싶은거면 saga 쓰는게 맞는거고.
redux-thunk: 객체 대신 함수를 생성하는 액션 생성함수를 작성 할 수 있게 해준다
https://ideveloper2.tistory.com/53
https://velog.io/@bigbrothershin/redux-thunk
그니까.. 일반 함수로 dispatch 다 할 수 있지만... 비동기적인 api 요청과 리덕스를 변경할 때 thunk를 쓰면 쉽게 비동기를 처리할 수 있다는거지.
뭐 굳이 하고 싶으면 일반 함수로도 쓸 수 있는데, dispatch와 getState가 인자로 넘어오는 thunk를 쓰면 쉽다는거지.
그럼 loading 끝났고 데이터가 있어.
ㅡㄴ데 다시 Refetch 하면 로딩은 사가 시작할 때 다시 true 만드는 건가?
끝나면 false 만들고?
그니까 graphql 처럼 그때그때 딱딱 맞게 loading, data, error 가 다 동작하냐 이거지.
---
미들웨어를 사용하는 이유: 비동기 처리를 위함이야.
근데 단순히 그냥 데이터 받고 setState 할거면 그렇게 해도되고,
만약 그 외에 loading, data, error와 뭐 여러 리듀서 실행, 및 등등 많은 작업을 하고 싶으면,
함수를 따로 생성해야 깔끔하겠지? 근데 그냥 일반 함수 하면 store랑 dispatch를 그때그때 넘겨줘야할거니까 좀 번거롭겠지?
그래서 액션과 리듀서 사이 중간자 역할을 해줄 수 있는 미들웨어를 사용해서 dispatch와 store에 쉽게 접근하고 사용하고 그 값을 변경하면서, 여러 비동기 작업을 처리할 수 있는거지.
이게 내가 이해한 미들웨어 사용 이유
이제 드디어 saga를 쓸 수 있을 것이야.
왜 thunk를 안쓰냐.
redux-saga:
복잡한 비동기 작업을 잘 쓸 수 있게 해줌
사이드 이펙트를 쉽게 관리
+ 더 검색해봐. 아마 비슷하겠지만.
=====
아무튼 이제 제대로 이해했어. 좀만 더 검색해보고 시작하자.
Logger 써보기
3-2. 개념서
react.vlpt.us/redux-middleware/10-redux-saga.html
- ducks 패턴
http://guswnsxodlf.github.io/redux-ducks-pattern
https://github.com/JisuPark/ducks-modular-redux
- Promise (개념서에 나오는 링크)
https://learnjs.vlpt.us/async/
- Generator 문법
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator
3-3. 리덕스 참고 블로그
https://jeonghwan-kim.github.io/dev/2019/07/22/react-saga-ts-1.html
리덕스 사가!!!!
1. 문서
react.vlpt.us/redux-middleware/10-redux-saga.html
다 봤음. 실제 활용 사례로는 턱없이 부족하다.
2-1. 블로그
jeonghwan-kim.github.io/dev/2019/07/29/react-saga-ts-2.html
=> 클래스 컴포넌트라 보기 불편
2-2. 블로그
chaewonkong.github.io/posts/react-redux-saga-typescript.html
[9.14 월]
따라해보기!
gracefullight.dev/2017/12/06/Why-redux-saga/
왜 thunk가 아닌가? 거기에는 return이 promise이기도 하고 암것도 리턴안하고 액션이랑 api랑 짬뽕되어있다.
사가는 액션리스너다. 액션을 디스패치하면 사가가 실행된다. 직관적이다. userInfo라는 애를 api로 가져올 때 액션리스너만 디스패치 하면 사가가 듣고 있다가 캐치해서 알아서 해주니까!
root에서는 spawn 을 써야 한대 ???
mskims.github.io/redux-saga-in-korean/advanced/ForkModel.html
주요 함수 정리
sustainable-dev.tistory.com/94
-----
폴더 구조
코드정리
// store/modules/join/userInfo.ts
import { UserInfoRes } from "~/utils/interface";
export interface RFetchResult<TData> {
// Redux Fetch Result
loading: boolean;
data: TData;
error: Error;
}
export interface UserInfoState {
userInfo: RFetchResult<UserInfoRes>;
}
const initialState: UserInfoState = {
userInfo: {
loading: false,
data: null,
error: null,
},
};
export const GET_USER_INFO_REQUEST = "GET_USER_INFO_REQUEST"; // 요청 시작
export const GET_USER_INFO_SUCCESS = "GET_USER_INFO_SUCCESS"; // 요청 성공
export const GET_USER_INFO_ERROR = "GET_USER_INFO_ERROR"; // 요청 실패
export const getUserInfoRequest = () => {
const fetchResult: RFetchResult<UserInfoRes> = {
loading: true,
data: null,
error: null,
};
return {
type: GET_USER_INFO_REQUEST,
userInfo: fetchResult,
};
};
export const getUserInfoSuccess = (userInfo: UserInfoRes) => {
const fetchResult: RFetchResult<UserInfoRes> = {
loading: false,
data: userInfo,
error: null,
};
return {
type: GET_USER_INFO_SUCCESS,
userInfo: fetchResult,
};
};
export const getUserInfoError = (error: Error) => {
const fetchResult: RFetchResult<UserInfoRes> = {
loading: false,
data: null,
error,
};
return {
type: GET_USER_INFO_ERROR,
userInfo: fetchResult,
};
};
export default (state = initialState, action): UserInfoState => {
switch (action.type) {
case GET_USER_INFO_REQUEST:
case GET_USER_INFO_SUCCESS:
case GET_USER_INFO_ERROR:
return { ...state, userInfo: action.userInfo };
default:
return state;
}
};
// store/modules/index.ts
import { combineReducers } from "redux";
import JoinReducer, { JoinState } from "./join";
export type RootState = {
join: JoinState;
};
const rootReducer = combineReducers({
join: JoinReducer,
});
export default rootReducer;
// saga/index.ts
import { all } from "redux-saga/effects";
import authSaga from "./auth";
import counterSaga from "./counter-test";
import { userInfoSaga } from "./join/userInfo";
export default function* rootSaga() {
yield all([counterSaga(), authSaga(), userInfoSaga()]); // all 은 배열 안의 여러 사가를 동시에 실행시켜줍니다.
}
// saga/join/userInfo.ts
import { takeEvery, call, put } from "redux-saga/effects";
import * as userInfoAPI from "~/api/join/userInfo";
import {
GET_USER_INFO_REQUEST,
getUserInfoSuccess,
getUserInfoError,
} from "~/store/modules/join/userInfo";
function* getUserInfoSaga() {
try {
const userInfo = yield call(userInfoAPI.getUserInfo);
yield put(getUserInfoSuccess(userInfo));
} catch (error) {
yield put(getUserInfoError(error));
}
}
// 사가들을 합치기
export function* userInfoSaga() {
yield takeEvery(GET_USER_INFO_REQUEST, getUserInfoSaga);
}
// api/join
export const getUserInfo = async (): Promise<UserInfoRes> => {
// try catch 하지 않음. saga에서 해주므로
const token = await getTokenItem();
if (!token) {
throw Error("client - no token");
}
const response = await fetch(`${BASE_URL}/accounts/`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const json: UserInfoRes = await response.json();
return json;
};
// store/index.ts
import { createStore, Unsubscribe, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootSaga from "./saga";
import rootReducer from "./modules";
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
export default store;
- 비동기를 처리하기 위해서 씀.
- 비동기:
- 동기: 리덕스는 결국 동기. 그게 처리되는 순간 아무것도 할 수 업성.
- 내부에서
리듀서는 순수함수여야 해.
순수함수? 항상 같은 값을 리턴하는 ㅏㅁ수
그걸 지켜주기 위해 괴상한 짓 하면 안돼
서버가 멈춰버리면
모든 서버와의 통신은 비동기적으로 처리해야해.
request success failure pattern
질문
이 과정을
순수함수?
redux saga entity pattern
action 하나하나가 기능이다
export enum aaaa {
request = 'GET_~~'
대괄호로 감싸면 key값으로 쓸 수 있다.
redux-actions
라이브러리
redux 편하게 쓸 수 있도록 메소드를 정의해놓은 라이브러리
immer: 불변성을 지켜줌 (라이브러리)
redux 세가지의 원칙
redux-actions
createAction 쓰면 type, action 안써줘도 돼 이미 추상화된 애거든
increment = () => ({type: sdafsdfa})
말고
increment = createActio(~~~~)b
import produce from 'immer'
produce()
이걸 쓰면 ...state 이거 안해줘도 되지
state.status
이걸로 상태관리
swich 에서 쓰는 case 같은거야
['key']
키 값을 변수로 쓸 수 있어
js에서 대괄호를 쓸 수 있어서 추상화
handleActions
...~~~해준다음
success만 다시 오버라이딩
saga gogogogogogo
fetchEntity !?
es6 generator ***
yield: 코드를 멈추는 아이.
{all, fork, takeLatest, put, call}
from redux-saga/effects
function* watchGetNotices() {
yield takeLatest()
}
yield는 코드를 block시켜서 while true를 쓴다.
takeEvery 가 while true랑 똑같대~ (머 약간은 다르나)
takeLatest
버튼을 계속 누르면 그걸 다 처리하지 않고
마지막 액션에 대해서만 동작을 실행한다
안심하는 느낌으로 쓰지만, 사실 여러번 누르면 그동안 컴퓨터가 다~~~ 처리해서
여러번요청 자체가 안되겠징
dispatch가 put
put이 redux state바꾸는 애.
draft 가 옆에 있는 stateㄹㄹ 받아서 그대로~~~ 쓰는거
그게 왜 비동기를 처리해야하는거지?
순간순간 요청들이 많다.
하나하나 기다
기다리지 않는 동안 로딩애니메이션을 보여주는거
로딩애니메이션 렌더
요청데이터 렌더
useSelector 렌더 (stater값 바뀌어서)
saga를 쓰면
똑같긴 한데
끝났다는걸 받어 (_SUCCESS)
그러면 손을 넣어서
스토어가 데이터를 받아서
한번 사이클을 만들어 놓으면 어디서든 쓸 수 있다.
- redux pattern 추상화
SUCESS, FAILURE, 이런거 해줬던 것처럼
${name}_SUCCESS
요론식으로~
비동기동기 아직도 헷갈림
watch는 ㅇ왜 함??
어떤 액션이 dispatch됐는지 보는 함수.
걔를 take
데이터 가져오려면 걍 dispatch하면돼. 평소처럼
그러면 saga는 전체 액션을 보고 있는데 (watch)
거기서 take 해서 바로바로 가져오는거지
- saga pattern 추상화
try call put catch put
fetchEntity로 하면돼!
watchGetNotices 안써도 됨
물론 saga를 안쓰고 걍 redux 바꿔도 되긴 하는데..
오히려 토이프로젝트엔 독일수도 있는데
점점 프로젝트 커지면서 유용할거야
yield는 await 이랑 비슷해?
역할은 다른데 하는건 비슷해
yield 는 멈춘다. 그래서 비동기적으로 처리하기 수월하다.
es6 비구조화 할당 const {} = asdfasdf
saga는 store를 바꾸는게 아니라
dispatch를 하는거야 이건 원래 알고 있는거지
saga: 지맘대로 동작하는 컴포넌트가 하나 더 있다!! 라고 생각ㄱ
https://dailyhotel.io/how-to-use-redux-saga-63a6078c74b3
https://begin-redux.vlpt.us/07-immer.html
https://jeonghwan-kim.github.io/dev/2019/07/22/react-saga-ts-1.html
===
https://medium.com/@han7096/redux-saga%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-5e39b72380af
ducks 패턴?
'Develop > React Native' 카테고리의 다른 글
[React Native] Animated 사용법 (내용없음) (0) | 2020.05.01 |
---|---|
[React Native] react-redux hooks / useMemo (내용 없음) (0) | 2020.05.01 |
[React Native] 현재 위치 위도/경도 알기 (0) | 2020.04.29 |
[React Native] 차트 (0) | 2020.04.17 |
[React Native] Google Login 구글 로그인 / 소셜 로그인 / 디버그용 릴리즈용 플레이스토어용 SHA-1 (0) | 2020.04.16 |