오랜만의 리팩토링이다. 어영부영하다가 미뤘는데, 오늘 생각나서 조금 건드려봄!
문서 생성후 breadCrumb가 바로 업데이트되지 않는 버그를 해결해보자.
single truth of source : 모든 데이터 요소를 한 군데에서만 조작해야함
나만의 리덕스를 만들고 useSelector, dispatch
를 사용했으나, 각 컴포넌트의 setState
로직과 혼재함.
=> 이때문에 문서 생성시 redux를 거치지 않았다.
// 문서 생성 로직
async createDocument(id = null) {
const body = { title: "제목 없음", parent: id };
const response = await request("/documents", {
method: "POST",
body: JSON.stringify(body),
});
this.getDocuments();
push(`/documents/${response.id}`);
return response;
}
//문서 생성 버튼 로직
onClick: async () => {
const response = await this.createDocument();
const storage = new Storage(window.localStorage);
storage.setItem(response.id, { isFolded: true });
},
그냥 단순하게 문서 생성후 push
를 이용하여 라우팅만 한다. 여기에 dispatch
를 도입해보자.
그러려면, 리덕스 모듈부터 바꿔주어야한다. Ducks패턴을 이용한 documentDucks
에는 문서를 생성하는 액션 생성자 함수가 없다.
생성또한 api를 이용하기때문에 비동기로 작업해야한다.
따라서 thunk 생성자 함수를 만든다. 이때 createDocument
에서 사용한 로직은 대부분 가져오면 됨!
export const createDocumentAsync =
(parentId = null) =>
async (dispatch) => {
try {
const body = { title: "제목 없음", parent: parentId };
const createdDocument = await request("/documents", {
method: "POST",
body: JSON.stringify(body),
});
dispatch({
type: CREATE_DOCUMENT,
payload: createdDocument,
});
} catch (e) {
console.error(e);
}
};
이렇게 만들고
페이지 추가버튼 누르니 생성은 된다.
되는데....
//Nav컴포넌트 render메서드에서 호출
const data = store.useSelector((state) => state.documentsReducer.documents);
const selectedDocument = store.useSelector(
(state) => state.documentsReducer.selectedDocument
);
//Nav컴포넌트 render메서드 내부에서 자식으로 호출하는 Button 컴포넌트에 props로 전달한다.
onClick: async () => {
store.dispatch(createDocumentAsync());
this.getDocuments();
const storage = new Storage(window.localStorage);
storage.setItem(selectedDocument.id, { isFolded: true });
},
//this.getDocuments 로직
getDocuments() {
store.dispatch(fetchDocumentsAsync());
}
왜 그런지 모르겠다.
하지만 this
는 보통 예기치 못한 문제를 불러일으킨다.
따라서 this.getDocuments
대신store.dispatch(fetchDocumentsAsync())
를 사용해봤다.
잘 해결됐다.
this.getDocuments.apply(this)
로 Nav
컴포넌트를 바인딩하자.
잘 해결됐다.
=> 프롭스로 전달할땐 this
를 명시적으로 바인딩해주자. 알고 있던 내용인데..ㅠㅠ
해결됐었는데, 다시 간헐적으로 업데이트가 안된다. 이전보다는 잘 되는데...
열심히 디버깅해보자...!!!
제목 그대로다! 문서 삭제까지 redux(나만의...)로 넘겨보자
//원래 로직
async removeDocument(id) {
await request(`/documents/${id}`, {
method: "DELETE",
});
this.getDocuments();
push("/");
}
//documentDucks에 추가된 thunk
export const removeDocumentASync = (documentId) => async (dispatch) => {
try {
if (!documentId) {
return alert("삭제하려는 문서 아이디를 지정해주세요");
}
const deletedDocument = await request(`/documents/${documentId}`, {
method: "DELETE",
});
if (deletedDocument) {
dispatch(fetchDocumentsAsync());
}
} catch (e) {
console.error(e);
}
};
dispatch내부에서 비동기 dispatch를 또 불러도 될까라는 확신이 없었지만...
결국 dispatch
를 받아와서 사용하기때문에 해도 된다고 본다!
selectedDocument
에 값이 남아있고 삭제한 문서라면 값을 날려주면 된다
case REMOVE_DOCUMENT: {
const selectedDocument = checkSelectedDocument(
action.payload,
state.selectedDocument?.id
)
? {}
: state.selectedDocument;
return {
...state,
selectedDocument,
};
}
reducer에서 하지않고 thunk함수쪽으로 넘겨주는 게 좋아보인다...!
일단 이렇게 해두자.
위의 step 2처럼 문서 삭제이후 바로 dispatch
를 이용하여 문서목록을 업데이트 해주었다.
이 로직을 문서 생성에도 적용해보자
=> 곰곰히 생각해보니, 문서 생성은 생성만 하고 삭제는 삭제만 해야한다.
명시적으로 호출해주는게 낫지 않을까 싶지만...일단 추가해봄!
export const createDocumentAsync =
(parentId = null) =>
async (dispatch) => {
try {
const body = { title: "제목 없음", parent: parentId };
const createdDocument = await request("/documents", {
method: "POST",
body: JSON.stringify(body),
});
dispatch({
type: CREATE_DOCUMENT,
payload: createdDocument,
});
if (createdDocument) {
dispatch(fetchDocumentsAsync());
}
} catch (e) {
console.error(e);
}
};
잘 됩니다!
지금까지 사용하던 push
메서드를 사용하면 될 것 같다.
//Nav.render() 일부
...
render() {
const data = store.useSelector((state) => state.documentsReducer.documents);
const selectedDocument = store.useSelector(
(state) => state.documentsReducer.selectedDocument
);
if (selectedDocument) {
push(`/documents/${selectedDocument.id}`);
}
...
}
//문서를 생성할땐
onClick: async () => {
store.dispatch(createDocumentAsync());
const storage = new Storage(window.localStorage);
storage.setItem(selectedDocument.id, { isFolded: true });
},
마치 useEffect처럼 렌더될때 선택된 문서로 이동시켜준다.
Nav렌더링할때
if (selectedDocument) {
push(`/documents/${selectedDocument.id}`);
}
이렇게 선택된 문서를 체크한다. 이때 문서가 객체여서 {}
는 truty로 판단되어 로직이 실행됨.
아래처럼 배열화하여 길이를 체크하면된다
if (Object.keys(selectedDocument).length) {
push(`/documents/${selectedDocument.id}`);
}
완성본은 여기서 볼수있다!
다음엔 컴포넌트를 분리하고 남아있는 버그도 수정해보겠다!(있는지 모르겠는데 있겠지...?)