리덕스

태권·2022년 8월 12일
0

개념알기

목록 보기
8/26

props와 state를 관리를 할 수있다.
리덕스는 여러 컴포넌트가 동일한 상태를 보고 있을 때 굉장히 유용합니다!
또, 데이터를 관리하는 로직을 컴포넌트에서 빼면, 컴포넌트는 정말 뷰만 관리할 수 있어서코드가 깔끔해질테니, 유지보수에도 아주 좋다.

상태관리 흐름도
Store, Action, Reducer, 그리고 Component

  • (1) 리덕스 Store를 Component에 연결한다.
  • (2) Component에서 상태 변화가 필요할 때 Action을 부른다.
  • (3) Reducer를 통해서 새로운 상태 값을 만들고,
  • (4) 새 상태값을 Store에 저장한다.
  • (5) Component는 새로운 상태값을 받아온다. (props를 통해 받아오니까, 다시 랜더링 되겠죠?)

yarn add redux react-redux
설치

리덕스는 데이터를 한 군데 몰아넣고, 여기저기에서 꺼내볼 수 있게 해준다.
State
리덕스에서는 저장하고 있는 상태값("데이터"라고 생각하셔도 돼요!)를 state라고 불러요.
딕셔너리 형태({[key]: value})형태로 보관
Action
상태에 변화가 필요할 때(=가지고 있는 데이터를 변경할 때) 발생하는 것입니다

{type: 'CHANGE_STATE', data: {...}}

요런식으로 쓴다.

ActionCreator
액션 생성 함수라고도 부릅니다. 액션을 만들기 위해 사용합니다.

//이름 그대로 함수예요!
const changeState = (new_data) => {
// 액션을 리턴합니다! (액션 생성 함수니까요. 제가 너무 당연한 이야기를 했나요? :))
	return {
		type: 'CHANGE_STATE',
		data: new_data
	}
}

Reducer
리덕스에 저장된 상태(=데이터)를 변경하는 함수입니다.
우리가 액션 생성 함수를 부르고 → 액션을 만들면 → 리듀서가 현재 상태(=데이터)와 액션 객체를 받아서 → 새로운 데이터를 만들고 → 리턴해줍니다.

// 기본 상태값을 임의로 정해줬어요.
const initialState = {
	name: 'mean0'
}

function reducer(state = initialState, action) {
	switch(action.type){

		// action의 타입마다 케이스문을 걸어주면, 
		// 액션에 따라서 새로운 값을 돌려줍니다!
		case CHANGE_STATE: 
			return {name: 'mean1'};

		default: 
			return false;
	}	
}

Store
스토어에는 리듀서, 현재 애플리케이션 상태, 리덕스에서 값을 가져오고 액션을 호출하기 위한 몇 가지 내장 함수가 포함되어 있습니다.
생김새는 딕셔너리 혹은 json처럼 생겼어요

dispatch
디스패치는 우리가 앞으로 정말 많이 쓸 스토어의 내장 함수예요!
액션을 발생 시키는 역할을 합니다.
dispatch(action)

리덕스의 3가지 특징
store는 1개만 쓴다 - 한 프로젝트에 스토어는 하나만 씁니다.
store의 state(데이터)는 오직 action으로만 변경할 수 있다 - 리액트에서도 state는 setState()나, useState() 훅을 써서만 변경 가능했죠!
데이터가 마구잡이로 변하지 않도록 불변성을 유지해주기 위함입니다.
불변성 뭐냐구요? 간단해요! 허락없이 데이터가 바뀌면 안된단 소리입니다!

조금 더 그럴 듯하게 말하면, 리덕스에 저장된 데이터 = 상태 = state는 읽기 전용입니다.

그런데... 액션으로 변경을 일으킨다면서요? 리듀서에서 변한다고 했잖아요?
→ 네, 그것도 맞아요. 조금 더 정확히 해볼까요!
가지고 있던 값을 수정하지 않고, 새로운 값을 만들어서 상태를 갈아끼웁니다!


어떤 요청이 와도 리듀서는 같은 동작을 해야한다
리듀서는 순수한 함수여야 한다는 말입니다.
순수한 함수라는 건,

  • 파라미터 외의 값에 의존하지 않아야하고,
  • 이전 상태는 수정하지(=건드리지) 않는다. (변화를 준 새로운 객체를 return 해야합니다.)
  • 파라미터가 같으면, 항상 같은 값을 반환
  • 리듀서는 이전 상태와 액션을 파라미터로 받는다.

덕스(ducks) 구조
덕스 구조는 모양새로 묶는 대신 기능으로 묶어 작성합니다.
(버킷리스트를 예로 들자면, 버킷리스트의 action, actionCreator, reducer를 한 파일에 넣는 거예요.)

// widgets.js

// Actions
const LOAD   = 'my-app/widgets/LOAD';
const CREATE = 'my-app/widgets/CREATE';
const UPDATE = 'my-app/widgets/UPDATE';
const REMOVE = 'my-app/widgets/REMOVE';

// Reducer
export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    // do reducer stuff
    default: return state;
  }
}

// Action Creators
export function loadWidgets() {
  return { type: LOAD };
}

export function createWidget(widget) {
  return { type: CREATE, widget };
}

export function updateWidget(widget) {
  return { type: UPDATE, widget };
}

export function removeWidget(widget) {
  return { type: REMOVE, widget };
}

// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
  return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget)))
}

Store 연결하기

import React from "react";
import ReactDOM from "react-dom/client";
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";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
    <Provider store={store}>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </Provider>
);

reportWebVitals();

컴포넌트에서 리덕스 액션 사용하는 법
리덕스 훅
// useDispatch는 데이터를 업데이트할 때,
// useSelector는 데이터를 가져올 때 씁니다.
import {useDispatch, useSelector} from "react-redux";

...
// redux 훅 중, useSelector를 가져옵니다.
import { useSelector } from "react-redux";

const BucketList = (props) => {
  let history = useHistory();
  //   이 부분은 주석처리!
  //   console.log(props);
  //   const my_lists = props.list;
>   // 여기에서 state는 리덕스 스토어가 가진 전체 데이터예요.
  // 우리는 그 중, bucket 안에 들어있는 list를 가져옵니다.
  const my_lists = useSelector((state) => state.bucket.list);
  return (
    <ListStyle>
      {my_lists.map((list, index) => {
        return (
          <ItemStyle
            className="list_item"
            key={index}
            onClick={() => {
              history.push("/detail");
            }}
          >
            {list}
          </ItemStyle>
        );
      })}
    </ListStyle>
  );
};
...

state는 리덕스 스토어가 가진 전체 데이터

//App.js

// useDispatch를 가져와요!
import {useDispatch} from "react-redux";
// 액션생성함수도 가져오고요!
import { createBucket } from "./redux/modules/bucket";

useDispatch 훅

const dispatch = useDispatch();

const addBucketList = () => {
    // 스프레드 문법! 기억하고 계신가요? :)
    // 원본 배열 list에 새로운 요소를 추가해주었습니다.
    // 여긴 이제 주석처리!
    // setList([...list, text.current.value]);

    dispatch(createBucket(text.current.value));
  };
profile
2022.08 개발자 시작

0개의 댓글