알고가기
1. Redux는 React에 종속된 라이브러리가 아닌 JavaScript의 state를 관리하기 위한 라이브러리이다.
2. Ducks 구조 : action, action 생성자, reducer이 하나의 파일에 들어가는 것.
Firestore에 임의로 구현한 유저의 프로필 정보, 게시글 내용 등의 데이터를 가져와서 값을 넘겨주는 함수를 구현하는데 이때 reduce함수를 사용하여 좀 더Javascript스럽게 코드를 구현해 보려고 한다.
Firebase (Cloud Firestore) 데이터.
import { createAction, handleActions } from 'redux-actions';
import { produce } from 'immer';
import { firestore } from '../../../shared/Firebase';
const initialState = { // 최종적으로 데이터의 값을 담을 배열 그릇이다.
list: [],
};
Redux의 action을 구현하다 보면 action 생성자를 하나씩 만들어야 하는 것을 볼 수 있다.
// 일반적인 action 형식
export const setPost = (post_list) => ({
type: types.SET_POST,
post_list
});
이는 파라미터로 전달받은 값을 객체에 넣어주는 단순한 패턴이다.
이런 귀찮고 단순 작업을 createAction을 이용해 해결시킬 수 있다.
// createAction 형식
function createAction(type, (parameter) => ({parameter}))
// 적용
export const setPost = createAction(SET_POST, (post_list) => ({post_list}))
handleActions를 사용하면 기존 switch로 작성한 방식보다 좀 더 간단히 코드를 작성할 수 있을뿐더러 scope가 reducer 함수로 설정되어 case 문에서 let, const의 변수를 선언하다 같은 이름이 중첩될 시 발생하는 에러를 보안할 수 있다.
//기존의 방식
const reducer = (state={}, action={}) => {
switch(action.type){
case SET_POST : {
state.list = action.post_list;
}
}
}
//handleActions 방식
const reducer = handleActions({
[SET_POST] : (state,action) => {
state.list = action.payload.post_list
// createAction()을 사용해서 기존 방식과 다른 payload를 써줘야한다.
// payload에 우리가 담은 값이 존재하게 된다.
}
},{}); // initialState = {}
React와 Redux는 setState 또는 dispatch 되었을 때 re-rendering이 일어난다.
불필요한 재렌더링은 웹 성능을 저하시킬 수 있기 때문에 useCallback을 사용하게 되는데
이 메서드는 state와 props의 변화를 감지해 변화가 일어날 경우만 렌더링을 허용한다!
여기서 불변성이 필요한 이유는 setState나 Dispatch를 이용해서 상태를 바꾸지 않는다면 re-rendering이 일어나지 않기 때문이다.
리덕스를 사용하는 이유 중 하나가 히스토리가 관리되기 때문에 redux의 state 값을 변경할 때는 불변성을 유지해 줘야 한다.
변경하기 전 state 값과 변경 후의 state의 값을 비교하기 위함이다.
// Immer 패키지의 produce() 사용하기.
export default handleActions({
[SET_POST] : (state, action) => produce(state, (draft) => {})
// draft가 있는 부분 : 원본 값을 가지고 어떠한 작업을 하고싶은지.
})
Immer는 A를 받고 복사해서 A1 을 만들게 되고 A대신 A1을 고치라고 한다.
이렇게 나온 A1을 불변성 유지를 신경쓰지 않고
const A = {a: 1, b: 2, c: 3};
A.b = 100;
A1이 아닌 A에 대하여 값을 변경시켜도 Immer는 알아서 값을 바꾼 A가 아닌 복사한 A1을 가르키고 불변성 유지를 시켜준다. (불변성 유지를 알아서 잘 해줌!)
const SET_POST = 'SET_POST';
const setPost = createAction(SET_POST, (post_list) => ({ post_list }));
export default handleActions(
{
[SET_POST]: (state, action) =>
produce(state, (draft) => {
// draft는 state의 값과 동일하며 값에 대한 불변성 유지를 위해 draft를 대신 사용한다.
draft.list = action.payload.post_list;
// payload는 어떤 타입의 값이든 가질 수 있다. 이는 action이 가지는 payload를 의미한다.
// type또는 status를 제외한 action의 정보는 모두 payload에 있어야 한다.
}),
},
initialState
);
defaultProps에 수기로 작성한 데이터 대신 firestore에 작성한 데이터들을 reducer의 SET_POST 함수를 통해 최종적으로 list 배열에 firestore 데이터를 넣어주는 함수이다.
const getPostFB = () => {
return function(dispatch, getState, { history }) {
const postDB = firestore.collection('post'); 📚
postDB.get().then((docs) => {
// 📚 postDB.get() 데이터를 가져옴 then() 가져온 데이터를 후처리 사용.
let post_list = [];
docs.forEach((docElements) => {
// Post.defaultProps = {..}의 키 값에 임의로 작성한 데이터를 firestore 데이터 값으로 적용하기
let _post = {
id: docElements.id,
...docElements.data(),
};
let post = {
id: _post.id,
user_info: {
user_name: _post.user_name,
user_profile: _post.user_profile,
user_id: _post.user_id,
},
image_url: _post.image_url,
contents: _post.contents,
comment_cnt: _post.comment_cnt,
insert_dt: _post.insert_dt,
};
post_list.push(post);
});
dispatch(setPost(post_list));
});
};
};