[Stack Overflow] 북마크 기능 (redux-toolkit)

hzn·2023년 1월 1일
0

PROJECT🏝

목록 보기
7/24
post-thumbnail
post-custom-banner

🍉 0. Provider 설정 & store 연결

  • store를 사용할 컴포넌트들 상위에서 Provider로 감싸준다

App.js

import { Provider } from 'react-redux';
import store from './store';
...
function App() {
  return (
    <Provider store={store}> // Provider 설정 & store 연결
      <div id="app">
        <Header />
        <main id="main">
          <Routes>
              <Route path="/" element={<Question />} />
              <Route path="/questions/:questionId" element={<DetailPage />} />
              <Route path="/posts/:questionId/edit/:answerId" element={<EditAnswerPage />} />
              <Route path="/bookmark" element={<BookmarkPage />} />
             ...
            </Route>
          </Routes>
        </main>
      </div>
    </Provider>
  );
}

  export default App;

🍉 1. Slice 만들기

1) createSlice 불러오기

2) Slice 만들기

(1) slice 이름 정하기 🌈
(2) 초기값 설정하기
(3) 리듀서 만들기
(리듀서: action의 type에 따라 상태를 변경시키는 함수)

3) action 자동 생성 문법을 구조분해할당으로 내보내기(export)

  • 이후 dispatch의 인자로 액션을 넣어줄 때 좀 더 간단한 형태로 쓸 수 있도록

bookmarkSlice.js

import { createSlice } from '@reduxjs/toolkit'; // 1) createSlice 불러오기

const bookmarkSlice = createSlice({ // 2) Slice 만들기
  name: 'myBookmarks', // slice의 이름 정하기
  initialState: { // 초기값 설정
    question_bookmarks: [
      {
        questionId: 300,
        title: '😀 Question Bookmark Sample',
        content:
          '🍉 Small question on Spring Boot, and how to use a design pattern combined with Spring @Value configuration in order to select the appropriate @Repository please.n',
        question_tag: ['mysql', 'database'],
        totalRecommend: 2,
        createdAt: 'Dec 19 at 5:48',
        member_id: 'Mimi',
      },
    ],
    answer_bookmarks: [
      {
        answerId: 100,
        content: 'Answer Bookmarks 샘플입니다',
        recommend: 2,
        createdAt: 'Dec 13 at 12:06',
        choose: false,
        member_id: 'Lily',
        questionId: 1,
      },
    ],
  },
  reducers: { // 리듀서 만들기
    add_question_bookmark: (state, action) => {
      state.question_bookmarks = [...state.question_bookmarks, action.payload];
    },
    remove_question_bookmark: (state, action) => {
      state.question_bookmarks = state.question_bookmarks.filter(
        (item) => item.questionId !== action.payload.questionId
      );
    },
    add_answer_bookmark: (state, action) => {
      state.answer_bookmarks = [...state.answer_bookmarks, action.payload];
    },
    remove_answer_bookmark: (state, action) => {
      state.answer_bookmarks = state.answer_bookmarks.filter(
        (item) => item.answerId !== action.payload.answerId
      );
    },
  },
});

export default bookmarkSlice;

export const { //3) 액션 자동 생성 문법을 구조분해할당으로 내보내기
  add_question_bookmark,
  remove_question_bookmark,
  add_answer_bookmark,
  remove_answer_bookmark,
} = bookmarkSlice.actions;

🍉 2. Store 만들기

Slice들을 모아서 하나의 Store 만들기

1) configureStore 불러오기

2) 만들어놓은 Slice들 불러오기

3) store 만들기

  • configureStore는 반드시 reducer를 키로 가지는 객체를 인자로 가진다.
  • reducer의 값은 객체이고, 이 객체의 키-값 쌍은 각각 하나의 slice에 대한 리듀서
  • 해당 키(= reducer의 이름, bookmarkReducer)는 이후 useSelector를 이용해 state 받아올 때 사용

Store.js

import { configureStore } from '@reduxjs/toolkit'; // 1) configureStore 불러오기
import bookmarkSlice from './detail/bookmarkSlice'; // 2) 만들어놓은 Slice들 불러오기

const store = configureStore({ // 3) store 만들기
  reducer: {
    bookmarkReducer: bookmarkSlice.reducer, // 해당 slice의 reducer 이름 정해주기 🌈 (이후 useSelector 이용해 상태 받아올 때 사용)
  },
});

export default store;

🍉 3. 상태 변경하기 : useDispatch

  • useDispatch()로 만든 dispatch 함수로 상태 업데이트

1) 필요한 액션 생성 문법 불러오기

  • 필요한 액션 생성 문법(action 타입 = case명의 형태) 중 필요한 것 불러오기
    (해당 컴포넌트에서 사용하는 액션 타입에 해당하는 것)

2) dispatch에 인자로 액션 전달

액션 자동으로 생성하는 문법

slice명.actions.case명(payload로 보낼 데이터)                 
  • 원래는 bookmarkSlice.actions.remove_question_bookmark(payload로 보낼 데이터) 형태이지만 위에서 구조분해할당 했으므로 remove_question_bookmark(payload로 보낼 데이터) 형태가 된다.

BookmarkPage.js

  • 북마크 삭제하기
import { useDispatch } from 'react-redux';
import { // 1) 북마크 삭제에 필요한 리듀서들 불러오기
  remove_answer_bookmark,
  remove_question_bookmark,
} from '../detail/bookmarkSlice';

export const BookmarkItem = ({ question, answer }) => { // 질문, 답변 데이터 인자로 전달
  const dispatch = useDispatch(); // useDispatch() 사용

  return (
    <DetailBody>
  ...
        <DeleteBtn>
          <BiTrash
            className="delete"
            onClick={() => {
              question && dispatch(remove_question_bookmark(question)); // 2) dispatch에 액션 전달
              answer && dispatch(remove_answer_bookmark(answer)); // 2) dispatch에 액션 전달
            }}
          />
        </DeleteBtn>
      </DetailFooter>
    </DetailBody>
  );
};

AdditionalFunc.js

  • 북마크 추가하기
import { useDispatch } from 'react-redux';
import {
  add_answer_bookmark,
  add_question_bookmark,
} from '../detail/bookmarkSlice';
...
export default function AdditionalFunc({ question, answer, isMyQuestion }) {
  const dispatch = useDispatch();
  const navigate = useNavigate();
...
return (
    <>
      {question && (
        <Container>
         ...
          <Icons>
            <FiBookmark
              className="icon"
              onClick={() => {
				dispatch(add_question_bookmark(question)); // dispatch에 인자로 액션 전달
                navigate('/bookmark');
              }}
            />
          </Icons>

🍉 4. state 가져와서 사용하기 : useSelector

1) 리듀서 이름(bookmarkReducer)으로 해당 Slice의 상태값 가져오기

2) 필요에 따라 상태값 객체 구조분해할당 하기

3) 가져온 상태값 사용하기

Bookmark.js

import { useSelector } from 'react-redux';

  export default function BookmarkPage() {
  const bookmarks = useSelector((state) => state.bookmarkReducer); // 1) 리듀서 이름으로 해당 Slice의 상태값 가져오기

  const { question_bookmarks, answer_bookmarks } = bookmarks; // 2) 상태값 객체 구조분해할당 하기

  return (
    <>
      <ContentsAndSideBox>
        <ContentsContainer>
          <Header>Question Bookmark 📚</Header>
          {question_bookmarks.map((question) => ( // 3) 가져온 상태값 사용하기
            <BookmarkItem key={question.questionId} question={question} />
          ))}
          <Header>Answer Bookmark 📚</Header>
          {answer_bookmarks.map((answer) => ( // 3) 가져온 상태값 사용하기
            <BookmarkItem key={answer.answerId} answer={answer} />
          ))}
        </ContentsContainer>
        <SideBox>
          <QuestionsSub />
        </SideBox>
      </ContentsAndSideBox>
    </>
  );
}
post-custom-banner

0개의 댓글