[React]리덕스(Redux)-2편

KBS·2021년 9월 26일
0

React

목록 보기
6/19
post-custom-banner

  • Redux 사용하는 법
  • 아래 포스팅은 내가 만든 프로젝트 코드를 바탕으로 설명 한다.

    • 보통 리덕스를 사용할 때는, 모양새대로 action, actionCreator, reducer를 분리해서 작성한다.
      (액션은 액션끼리, 액션생성함수는 액션생성함수끼리, 리듀서는 리듀서끼리 작성한다!)
    • 덕스 구조는 모양새로 묶는 대신 기능으로 묶어 작성한다.
      (버킷리스트를 예로 들자면, 버킷리스트의 action, actionCreator, reducer를 한 파일에 넣는다!!!)
    yarn add redux react-redux

    리액트 파일을 생성후 위와 같이 redux패키지를 설치한다.

    외부의 데이터와 연동하고 싶다면 thunk 까지 다운 받는다

    yarn add redux-thunk

    index.js

    다음으로 index.js 파일에서 아래와 같이 세팅 하는데,

    import React from "react";
    import ReactDOM from "react-dom";
    import "./index.css";
    import App from "./App";
    import reportWebVitals from "./reportWebVitals";
    import { BrowserRouter } from "react-router-dom";
    import { Provider } from "react-redux";
    import store from "./redux/configStore";
    
    ReactDOM.render(
      <Provider store={store}>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </Provider>,
    
      document.getElementById("root")
    );
    
    reportWebVitals();
    

    redux를 Provider로 import 한 후에, render 부분에서

    <Provider store={store}> </Provider>
    

    위의 태그로 감싸면 된다.

    word.js

    위치: src/redux/modules/word.js

    다음으로 action, actionCreator, reducer 를 담은 파일
    word.js 파일의 코드를 보면

    import {
      collection,
      doc,
      getDoc,
      getDocs,
      addDoc,
      deleteDoc,
    } from "firebase/firestore";
    import { db } from "../../firebase";
    // 액션
    const LOAD = "words/LOAD";
    const ADD = "words/ADD";
    const initialState = {
      words: [],
    };
    // 액션 생성 함수
    export const loadWords = (words_list) => {
      return { type: LOAD, words_list };
    };
    export const addWords = (words_list) => {
      return { type: ADD, words_list };
    };
    // 미들웨어
    export const loadWordsFB = () => {
      return async function (dispatch) {
        const words_data = await getDocs(collection(db, "words"));
        let words_list = [];
        words_data.forEach((words) => {
          words_list.push({ ...words.data() });
        });
        dispatch(loadWords(words_list));
      };
    };
    export const addWordsFB = (words_list) => {
      return async function (dispatch) {
        console.log(words_list);
        await addDoc(collection(db, "words"), words_list);
        // dispatch(addWords(words_list))
      };
    };
    //리듀서
    export default function reducer(state = initialState, action = {}) {
      switch (action.type) {
        case "words/LOAD": {
          return { ...state, words: action.words_list };
        }
        case "words/ADD": {
          const new_words = [...state.words, action.words_list];
          return { ...state, words: new_words };
        }
        default:
          return state;
      }
    }
    

    위의 코드의 주석대로 기능을 묶어서 작성한다.
    위의 코드는 단순히 input 값을 Add 하고, Load 하는 기능을 구현했다.

    그리고 중간의 미들웨어는 FIrebase와 연동하기 위한 작업이다.
    파이어베이스와 연동하기 위해서는 firebase.js 라는 파일을 만들고, 그 안에 Firebase 에서 제공하는 연동 코드를 붙여넣으면 된다.

    configStore.js

    위치: src/redux/configStore.js

    다음 작업으로는 export default 한 리듀서를 받아와 묶어서(rootReducer)
    store를 만드는 작업이 남았다. 아래의 코드는 configStore.js라는 파일이다.

    // Store의 기본 설정이라고 생각하면됨, combineReducers은 리듀서를 묶어주는 역할을 한다.
    // Store를 만들기 위해 createStore가 필요하다.
    import { createStore, combineReducers, applyMiddleware } from "redux";
    import thunk from "redux-thunk";
    
    // 리듀서를 가져온다.
    import word from "./modules/word";
    
    const middlewares = [thunk];
    // 리듀서를 다 묶은 것을 rootReducer라고 한다.
    const rootReducer = combineReducers({ word });
    const enhancer = applyMiddleware(...middlewares);
    
    // rootReducer가지고 store를 만들어준다.
    const store = createStore(rootReducer, enhancer);
    
    export default store;
    

    Add.js

    Add.js 파일은 input 안에 값을 넣고 Button 을 누르면 그 값이 Firebase 에 저장 되도록 하였다.

    그리고 word.js 에서 생성했던 미들웨어 함수 addWordFB를 import 해와서 Button 클릭시 input에 입력된 값을 전달하도록 한다!! (dispatch 이용)

    import React from "react";
    import { useHistory } from "react-router-dom";
    import { useSelector, useDispatch } from "react-redux";
    import { addWords, addWordsFB } from "./redux/modules/word";
    
    const history = useHistory();
      const dispatch = useDispatch();
      const word_ref = React.useRef(null);
      const explanation_ref = React.useRef(null);
      const ex_ref = React.useRef(null);
    //input 태그 생략
    .// 전체 코드 아님 주의
    .
    .
    
     <button
            onClick={() => {
              dispatch(
                addWordsFB({
                  word: word_ref.current.value,
                  explanation: explanation_ref.current.value,
                  ex: ex_ref.current.value,
                })
              );
    
              history.push("/");
            }}

    history.push를 통해 url을 변경한다.(라우팅)

    Home.js

    Home.js 파일은 파이어베이스에 저장되어있는 데이터들을 불러와 화면에 나타내주기 위한 파일이다!
    이 파일은 전체 코드를 붙여봤다. 이유는 다양한 기능을 동시에 설명하고 싶어서..

    아래의 코드에서 쓰인 기능 중 styled-componets 는 react 에서 css를 좀 더 편하게 사용할 수 있도록 도와준다.

    styled components 코드는 맨 하단의 const로 변수를 만들어 그것을 태그 이름으로 하도록 할 수 있다.

    import React from "react";
    import styled from "styled-components";
    import { useSelector, useDispatch } from "react-redux";
    import { useParams } from "react-router";
    import {
      border,
      width,
    } from "../../SPARTA_REACT/bucket_list/node_modules/@mui/system";
    import { useHistory } from "react-router-dom";
    
    import { loadWordsFB } from "./redux/modules/word";
    
    const Home = ({ list }) => {
      const word_list = useSelector((state) => state.word.word_list);
      const dispatch = useDispatch();
      console.log(word_list);
      const history = useHistory();
    
      React.useEffect(() => {
        dispatch(loadWordsFB());
      }, []);
    
      const _word_data = useSelector((state) => state.word.words);
      console.log(_word_data);
    
      return (
        <div>
          {_word_data.map((dictionary, index) => {
            return (
              <Cards key={index}>
                <Words>
                  <p>단어</p>
                  <p style={{ border: "1px solid #ddd" }}>{dictionary.word}</p>
                  <p>설명</p>
                  <p style={{ border: "1px solid #ddd" }}>
                    {dictionary.explanation}
                  </p>
                  <p>예시</p>
                  <p style={{ border: "1px solid #ddd", color: "lightsteelblue" }}>
                    {dictionary.ex}
                  </p>
                </Words>
              </Cards>
            );
          })}
          <AddButton
            onClick={() => {
              history.push("/add");
            }}
          >
            +
          </AddButton>
        </div>
      );
    };
    
    const AddButton = styled.button`
      width: 60px;
      height: 60px;
      background-color: #a673ff;
      border-radius: 100px;
      border: 0px;
      color: #fff;
      font-size: 83px;
      position: fixed;
      display: flex;
      align-items: center;
      right: 8%;
      bottom: 15%;
    `;
    
    const Words = styled.div`
      width: 200px;
      background-color: lightyellow;
      border: 1px solid #ddd;
      padding: 10px;
      height: 250px;
      margin: 0;
      margin: 20px;
    `;
    
    const Cards = styled.div`
      margin-top: 15px;
      display: flex;
      justify-content: center;
      align-items: center;
      float: left;
    `;
    export default Home;
    

    위의 코드에서 잘 보면 아래의 dispatch 를 통해 파이어베이스의 데이터를 호출하는 함수 loadWordsFB 함수를 word.js 에서 불러와 일으킨다.

    React.useEffect(() => {
        dispatch(loadWordsFB());
      }, []);

    다음으로 아래의 코드를 통해 변수에 그 값들을 저장한다.

    const _word_data = useSelector((state) => state.word.words);

    위의 화살표 뒤의 코드 sate.word(js파일 이름).words(word.js의 리듀서에서 return 한 리스트 이름)

    이제 불러온 값들을 아래와 같이 map을 이용해 index별로 받아와 값을 나타낸다.

    {_word_data.map((dictionary, index) => {
            return (
              <Cards key={index}>
                <Words>
                  <p>단어</p>
                  <p style={{ border: "1px solid #ddd" }}>{dictionary.word}</p>
                  <p>설명</p>
                  <p style={{ border: "1px solid #ddd" }}>
                    {dictionary.explanation}
                  </p>
                  <p>예시</p>
                  <p style={{ border: "1px solid #ddd", color: "lightsteelblue" }}>
                    {dictionary.ex}
                  </p>
                </Words>
              </Cards>
            );
          })}

    위와 같은 과정들을 거치고난 결과 화면

    단어장 완성!!

    profile
    FE DEVELOPER
    post-custom-banner

    0개의 댓글