Today I Learned 2023.03.04. [React 숙련주차 2]

Dongchan Alex Kim·2023년 3월 4일
0

Today I Learned

목록 보기
9/31
post-thumbnail

Redux

전역상태 관리 라이브러리이며, 중앙 state관리소라고 생각하면 편하다. 모든 state를 여기서 생성한다.
useState(Local State) <-> Redex(Global state)

yarn add redux react-redux

Redux에서는 폴더구조가 굉장히 중요하다
1. src 안에 redux폴더 생성
2. redux폴더 -> config폴더modules폴더
3. config폴더 -> configStore.js

  • redux폴더 : 리덕스와 관련된 코드를 모두 모아 놓을 폴더.
  • config폴더 : 리덕스 설정과 관련된 파일들을 놓을 폴더.
  • configStore.js : 중앙 state 관리소" 인 Store를 만드는 설정 코드.
  • modules : 우리가 만들 State들의 그룹이 들어감.
// configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux";

//createStore()이랑 combineReducers() 이 뭔지 알려하지마!!!
//생각하지말고 그냥 무지성으로 쓰기.


const rootReducer = combineReducers({/* 여기 안에 리듀서 함수 넣는다.*/}); 
const store = createStore(rootReducer); 

export default store; 
//index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

//추가할 코드
import store from "./redux/config/configStore";
import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(

	//App을 Provider로 감싸주고, configStore에서 export default 한 store를 넣는다.
  <Provider store={store}> 
    <App />
  </Provider>
);

reportWebVitals();

Module의 구성 알아보기

  1. initialState : 초기 상태 값 설정
    -> 이 부분은 reducer 함수의 return 부분에 순수함수로서 형태를 맞춰야하기 때문에, 초기 설정값이 매우 중요하다고 볼 수 있겠다.

  2. Reducer : 함수 모음집

// counter.js

// counter 리듀서
const counter = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

export default counter;
  • reducer 함수에는 두개의 인자를 받는데, 첫번째 인자로는, state를 받고 두번째 인자로는 action을 받는다.
    state는 initialState가 들어가면 되겠다.
    action의 경우는 dispatch로 받은 값 전체가 할당된다.
//configStore.js

import counter from "../modules/counter";

const rootReducer = combineReducers({
  counter: counter, // <-- 새롭게 추가한 부분
});
const store = createStore(rootReducer);

export default store;

만든 모듈을 스토어에 연결시켜야 한다.

  1. useSelector
    컴포넌트에서 store를 조회할 때에는 useSelector라는 react-redux에서 제공하는 useSelector라는 hook을 이용한다.
//App.js
import React from "react";
import { useSelector } from "react-redux";

const App = () => {
  const counterStore = useSelector((state) => state); 
  console.log(counterStore); // 스토어를 조회

  return <div></div>;
}

export default App;

스토어에서 reducer 함수를 counter라고 store 연결시켰기 때문에, 콘솔에 찍히는 값은 리듀서 함수와 그 값들이 되겠다.

action 객체 내보내기

useDispatch
useDispatch라는 훅을 이용해서 리듀서로 보내게 된다.

// src/App.js

import React from "react";
import { useDispatch } from "react-redux"; 

const App = () => {
  const dispatch = useDispatch(); // dispatch 생성
  return (
    <div>
      <button
        onClick={() => {// ()안에 있는 액션객체가 리듀서로 전달된다.
          dispatch({ type: "PLUS_ONE" }); 
        }}
      >
		+ 1
      </button>
    </div>
  );
};

export default App;

reducer함수가 들어있는 파일로 이동하여, switch문을 잘 작성해주기만 하면 끝!
switch 문의 조건에는 action의 type값을 받아올 수 있도록 할당하며, case에는 type에 넣었던 string 문자열을 그대로 넣어준다. type값을 조회할 때, 이 문자열과 비교하여 실행할지 말지를 결정하기 때문에, human error가 나지 않도록 조심하자.

//counter.js
const initialState = {
  number: 0,
};

const counter = (state = initialState, action) => {
  console.log(action);
  switch (action.type) {
    case "PLUS_ONE":
      return {
        number: state.number + 1,
      };

    default: // default값을 추가하는 버릇을 키워보자.
      return state;
  }
};

export default counter;

그럼 dispatch를 이용해서 state값을 변경했으니, store에서 바뀐 state값을 조회해보자.
조회는 뭐다? redux 의 기본 hook인 useSelector를 이용해서 가져온다.

const number = useSelector((state) => state.counter.number); 
  • Action Creator
  1. 휴먼 에러 방지 (자동완성 기능 사용가능)
  2. 유지보수의 효율성 증가 -> Action들을 한눈에 볼 수 있다.
  3. (2)에서 말한 거랑 비슷한데, 가독성이 좋아진다.
//counter.js

// Action value를 상수들로 만들어 한곳에 모은다.
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";


// Action Creator를 만든다. 
export const plusOne = () => {
  return {
    type: PLUS_ONE,
  };
};

export const minusOne = () => {
  return {
    type: MINUS_ONE,
  };
};


const initialState = {
  number: 0,
};


const counter = (state = initialState, action) => {
  switch (action.type) {
    case PLUS_ONE: // case에서도 위에서 만든 상수를 넣어버려. 
      return {
        number: state.number + 1,
      };
    case MINUS_ONE:
      return {
        number: state.number - 1,
      };
    default:
      return state;
  }
};


export default counter;

payload

state를 변경하는데 있어 만약 리듀서에게 어떤 값을 같이 보내줘야 한다면 payload를 액션객체에 같이 담아 보내는 것이다.

  1. action creator
//counter.js

// Action Value
const ADD_NUMBER = "ADD_NUMBER";

// Action Creator
export const addNumber = (payload) => {
  return {
    type: ADD_NUMBER, // type에는 위에서 만든 Action Value를 넣어준다.
    payload: payload, // payload의 경우에는 인자로 받아와서 return 해줄 수 있다.
  };
};
//reducer
const counter = (state = initialState, action) => {
  switch (action.type) {
    case ADD_NUMBER:
      return {
        number: state.number + action.payload,
      };
    default:
      return state;
  }
};

react-router-dom

페이지 전환에 주로 쓰이는 패키지라고 생각하면 되겠다.

yarn add react-router-dom 

react-redux와 마찬가지로, react-router-dom도 폴더 구조가 매우 중요한다.

  1. pages폴더 생성 -> 안에 여러 페이지 만들기 ex) Home.jsx , About.jsx, Contact.jsx
  2. shared 폴더 생성 -> 안에 Router.js 생성 -> router 설정 코드 작성
  3. App.js 에서 import 해버리기

Router.js
-> 브라우저에 우리가 URL을 입력하고 이동했을 때 우리가 원하는 페이지 컴포넌트로 이동하게끔 만드는 부분이다.

//Router.js

import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";

const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="contact" element={<Contact />} />
        <Route path="works" element={<Works />} />
      </Routes>
    </BrowserRouter>
  );
};

export default Router;

Routes안에 있는 Route에는 react-router-dom에서 지원하는 props들이 있다. path는 원하는 "주소"를 넣어주면 된다.
element는 해당 주소로 이동했을 때 보여주고자 하는 page 컴포넌트를 작성해주면 되겠다. (import 꼭 해주기!)

//App.js
import React from "react";
import Router from "./shared/Router";

function App() {
  return <Router />;
}

export default App;

Router.js를 App 컴포넌트에 넣어주는 이유?

생각하지 말고 걍 무조건 App.js를 거쳐야 한다. path 별로 분기가 되는 Router.js를 App.js에 위치시키고, 이 페이지를 이용하는 모든 사용자가 무조건 App.js → Router.js 이런 식으로 이동할 수 있겠금 해야만 한다!!!!

react-router-dom Hook

  1. useNavigate() : 어떤 버튼을 누르면 페이지로 이동하거나 또는 어떤 컴포넌트를 눌렀을 때 페이지를 이동하게 만드는 훅이다.
//Home.js
import { useNavigate } from "react-router-dom";

const Home = () => {
  const navigate = useNavigate();

  return (
    <button
      onClick={() => {
        navigate("/works");
      }}
    >
      works로 이동
    </button>
  );
};

export default Home;
  • Dynamic Route란?
    path에 유동적인 값을 넣어서 특정 페이지로 이동하게끔 구현하는 방법을 말한다.
    기본적인 설정 방법은
    하위 페이지를 만들고,(work.jsx)
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";
import Work from "../pages/Work";

const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="contact" element={<Contact />} />
        <Route path="works" element={<Works />} />
        <Route path = "works/:id" element ={<Work/>}/>
      </Routes>
    </BrowserRouter>
  );
};

export default Router;

최적화를 위해 데이터는 다른 파일로 빼주는 연습을 해본다.

//data.js

export const data = [
  {
  id : 1,
  todo : '리액트 배우기', 
  },
  {
  id : 2,
  todo : '노드 배우기', 
  },
  {
  id : 3,
  todo : '스프링 배우기', 
  },
  {
  id : 4,
  todo : '파이어 베이스 배우기', 
  },
  {
  id : 5,
  todo : '데이터 베이스 배우기', 
  },
  {
  id : 6,
  todo : 'next.js 배우기', 
  }
];

data를 다른 파일로 빼준거를 이제 work와 works 두개에 모두 import를 해주어야 한다.

//Works.js
import React from 'react';
import { Link, useParams } from 'react-router-dom';
import data from "../shared/data/";

function Works() {
  return (
    <div>
    	할일목록
      {data.map((work) => {
        return (
          <div key={work.id}>
            <div> 할일: {work.id}</div>
            <Link to={`/works/${work.id}`}>
              <span style={{ cursor: 'pointer' }}>➡️ Go to: {work.todo}</span>
            </Link>
          </div>
        );
      })}
    </div>
  );
}
export default Works;

상위 컴포넌트인 Work.jsx에 data를 import 해주고, map함수를 이용해서 data배열을 순회하면서 불러오도록 해보자.
<Link> 태그를 이용해서 하위 컴포넌트로 이동하는 path를 지정해줄 수 있다.
"<Link to={/works/${work.id}}>"로 경로를 설정해주면, 라우터에서 지정한 id값으로 지정되어 하위페이지를 불러온다.

function Work() {
  const param = useParams();

  const work = data.find((work) => work.id === parseInt(param.id));

  return <div>{work들어가서 비교할 수 있는 조건으로 함수를 만들 수 있다.
```jsx
const work = data.find((work).todo}</div>;
}

export default Work;

useParams 훅을 이용해서 불러온 parameter를 가져온다.
-> Router.js에서 설정한 Workd/:id에서 id로 parameter를 설정해놨기 때문에 하위페이지로 넘어왔을때, 이게 무슨 페이지인지 useParams를 이용해서 확인할 수 있게 된 것이다.

  • data.find() API
    : 콜백함수로 이터러블 하나하나가 => work.id === parseInt(param.id));

리덕스 끝! 전역하고 와야징

profile
Disciplined, Be systemic

0개의 댓글