11-1.Redux-basic

Updated:


Latest update : 2021-10-1 (Redux basic) - 4.Action 부터

  • Redux 를 쓰는 이유..

    1.복잡한 props 전송이 필요 없음 2.모든 컴포넌트가 직접 데이터를 꺼내서 쓸 수 있음 3.state 데이터 관리 기능

1.설치 및 사용

$ yarn add redux react-redux

redux 는 데이터를 엄격하게 관리하는 기능, react-redux 는 리덕스를 리엑트에서 쓸 수 있게 도와주는 기능을 제공

1-1.index.js 에서 redux 초기 설정

redux를 이용한 개발환경을 셋팅하시려면 index.js를 열어 다음과 같이 작성합니다..

// in index.js

import { Provider } from "react-redux";

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);
  • 라는걸 import 해오신 다음에
  • 내가 state값 공유를 원하는 컴포넌트를 다 감싸시면 됩니다.

  • 컴포넌트를 감쌌습니다.

그럼 컴포넌트와 그 안에있는 모든 HTML, 컴포넌트들은 전부 state를 직접! props 전송없이! 사용할 수 있습니다.

🔷 이것이 redux를 이용하는 첫번째 이유입니다

컴포넌트가 매우 깊숙히 있다면 state전달하려고 props 100번 써야되고 귀찮은데

redux를 이렇게 셋팅해주시면 props 100번 쓰실 필요가 없습니다. 바로 꺼내쓰실 수 있습니다.

(Context 그 문법이랑 매우 비슷합니다.)


📌 redux에서 state를 하나 만드시려면 createStore() 함수를 쓰셔야합니다.

import { Provider } from "react-redux";
import { createStore } from "redux";

let store = createStore(() => {
  return [{ id: 0, name: "niceShoes", quan: 2 }];
});

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);

import 해오신 다음에 createStore(콜백함수) 이렇게 사용하시면 되며

콜백함수엔 뭘 작성하냐면.. 내가 원하는 state 초기값으로 내보낼 값을 정해서 .

그럼 store 에 저장된 state 를 사용할 수 있습니다.


이제 <Provider> 에 만든 state 를 props 처럼 등록해서 사용하면 됩니다.

import { Provider } from "react-redux";
import { createStore } from "redux";

let store = createStore(() => {
  return [{ id: 0, name: "niceShoes", quan: 2 }];
});

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);

redux 설치 후엔 state들을 store라는 명칭으로 부릅니다. 아까 변수명이 store이기도 하고요

1-2.store에 있는 state 데이터 꺼내쓰는 법

  • 예시로 Cart.js 라는 파일을 만들어 table 생성 후, 그 테이블 안에 store 에 저장된 데이터를 데이터 바인딩해서 사용하기

  • 그냥 바로 사용하면 안되고 store 안에 있는 데이터를 props 의 형태로 등록해서 사용해야 합니다.

// connect 를 import 해옴 redux 로 부터
import { connect } from "react-redux";
function Cart(props) {
  return (
    <div>
      // react bootstrap 으로 table 하나 생성함
      <Table>
        <tr>
          <th>#</th>
          <th>상품명</th>
          <th>수량</th>
          <th>변경</th>
        </tr>
        <tr>
          <td>1</td>
          // 아래와 같이 state 를 props 화 시켜서 원하는 data 를 바인딩해서 쓸 수
          있음
          <td>{props.state[0].name}</td>
          <td>Table cell</td>
          <td>Table cell</td>
        </tr>
      </Table>
    </div>
  );
}

// state 를 props 화 해서 쓰는것
let storeData = (state) => {
  return {
    state,
  };
};

// 이거는 connect 를 사용해서 store 에 연결 시키고 이거는 패턴이니 잘 알아둬야 함
export default connect(storeData)(Cart);

1-3.setting 요약

셋팅은

1.index.js에 를 import 해오신 다음

2.state 값공유를 원하는 컴포넌트를 감싸면 됩니다.

3.createStore를 import 해오신 다음 사용법에 의해 state를 만들어 let store라는 변수에 저장합니다.

4.<Provider store={store}> 이렇게 store를 등록하면

이제 Provider로 감싼 컴포넌트는 전부 store안에 있던 값을 props없이 공유 가능합니다.

store안에 있던 state 사용은

원하는 컴포넌트 파일 가셔서

1.하단에 function state를props화() 를 하나 만들어주고 state를 props로 등록합니다.

2.그리고 또 하단에 export default connect(state를props화)(Cart);

이렇게 사용하시면 이제 아까 만들어둔 state가 props로 등록이 된 것입니다.

props.state이름 이렇게 저장된 state를 자유롭게 사용할 수 있습니다.

이와 같이 한번 셋팅하고 오나료된 모든 component 는 redux 내에서 state 를 자유지재로 사용 가능합니다.

image

2.reducer,dispatch 로 데이터 수정하는 법

  • redux 에선 state 데이터의 수정방법을 미리 정의합니다. : reducer

아래 예제는 수량을 + 버튼 누르면 수량 증가, - 버튼을 누르면 수량 감소하는 reducer 만들기

// index.js

// state 원본 자료
const defaultState = [
  { id: 0, name: "niceshoe", quant: 2 },
  { id: 1, name: "niceshoe2", quant: 1 },
];

// state 수정을 위한 reducer 생성
const reducer = (state = defaultState, action) => {
  if (action.type === "quanIncrease") {
    const copy = [...state]; // deep copy 한 state 만들어서
    copy[0].quant++; // 첫번째 id 에서 수량이 바뀌게 만들기
    return copy; // 변경된 data 인 copy state return
  } else if (action.type === "quanDecrease") {
    const copy = [...state];
    copy[0].quant--;
    return copy;
  } else {
    // 변경된것이 없을경우 그냥 state return
    return state;
  }
};

const store = createStore(reducer); // store 생성

데이터 수정하기

// in cart.js

// 버튼 클릭시, 수량이 증가되게 만들기 (dispatch 사용해서 store data 가져오기)
<button onClick={() => { props.dispatch({ type: 'quanIncrease' }) }}>+</button>
<button onClick={() => {props.dispatch({type: 'quanDecrease'})}}>-</button>

let storeData = state => {
  return {
    state
  }
}

export default connect(storeData)(Cart);

3.state 와 reducer 가 더 필요할 경우

  • reducer 를 하나 더 만들경우 combineReducers 를 통해서 파일을 reducer 들을 묶어 줘야 함

  • 예제 alert 창 만들어서 닫기 동작 버튼 만들기

// index.js

const defaultAlert = true;

const reducer2 = (state = defaultAlert, action) => {
  if (action.type === "alertClose") {
    state = false;
    return state;
  } else {
    return state;
  }
};

// 기존 reducer 와 함꼐 사용하기 위해서 combineReducers 사용함
const store = createStore(combineReducers({ reducer, reducer2 }));

2021-10-1

추가 업데이트 Redux Basic

4.Action

  • Redux 에서 action 은 사실 그냥 객체 (Object) 입니다.

  • store 에 전달이 되면 스토어의 상태를 변경하느데 사용됩니다.

  • 두가지 형태의 액션이 있습니다.

    • {type: ‘TEST’} // payload 없는 액션

    • {type: ‘TEST’, params: ‘hello’} // payload 있는 액션

  • type 만이 필수 프로퍼티 이며, type 은 문자열 입니다.

redux 액션 생성자

function 액션생성자 (…arg) {return 액션} 이런식의 패턴으로 action 을 생성합니다.

  • 액션을 생성하는 함수를 ‘액션 생성자 (Action Creator)’ 라고 합니다.

  • 함수를 통해 액션을 생성해서, 액션 객체를 리턴해줍니다.

  • createTest(‘hello’); // {type: ‘TEST’, params: ‘hello’} 리턴

리덕스에서 action은 어떠한 일은 하는가?

  • action 생성자를 통해 action을 만들어 냅니다.

  • 만들어낸 action 객체를 리덕스 스토어에 보냅니다

  • 리덕스 트오어가 action 객체를 받으면 스토어의 상태 값이 변경 됩니다.

  • 변경된 상태 값에 의해 상태를 이용하고 있는 컴포넌트가 변경됩니다.

  • action은 스토에 보내는 일종의 input 이라고 생각하면 됩니다.

action을 준비하기 위해서는?

  • action의 타입을 정의하여 변수로 빼는 단계

    • 강제는 아닙니다. (그러므로 안해도 됩니다)

    • 그냥 타입을 문자열로 넣기에는 실수를 유발할 가능성이 큽니다.

    • 미리 정의한 변수를 사용하면, 스펠링에 주의를 덜 기울여도 됩니다.

  • action 객체를 만들어 내는 함수를 만드는 단계

    • 하나의 action 객체를 만들기 위해 하나의 함수를 만들어냅니다.

    • action의 타입은 미리 정의한 타입 변수로 부터 가져와서 사용합니다.

보통 actions 의 type 은 대문자에 underbar _ 를 사용해소 표현합니다.

// 예시 add_todo 라는 객체를 만들어서 todo 에 집어 넣는 예시

export const ADD_TODO = "ADD_TODO";

const addTodo = (todo) => {
  return {
    type: ADD_TODO,
    todo,
  };
};

5.Reducer

  • action 을 주면, 그 action 이 적용되어 달라진(안달라질수도 있긴한데.) 결과를 만들어 줍니다.

  • 그냥 함수라고 보면 됩니다.

    • Pure function : 같은 input 을 받으면 같은 결과를 내는 것

    • Immutable : reducer 를 통해 스테이트가 달라졌을을 redux 가 인지하는 방식 - 원래의 객체와 새로 만들어진 객체가 별도로 만들어져야 된다는 것

reducer 형태

function 리듀서(previousState, action) {
  return newState;
}
  • action 을 받아서 state 를 return 하는 구조

  • 인자로 들어오는 previousState 와 리턴되는 newState 는 다른 참조를 가지도록 해야 합니다.

// reducer 작성 예시

import { ADD_TODO } from "./actions";

// state
// ['test', 'test2']; 이런식의 형태로 state 가 되어 있음

const initialState = [];
// previousState 가 undefined 아무것도 안들어 올경우 initialState 로 초기화를 자동으로 해줌
export const todoApp = (previousState = initialState, action) => {
  // 기존 action 의 type 이 ADD_TODO 와 같을 경우 이전것과 action.todo 를 합쳐서 return 해줌
  if (action.type === ADD_TODO) {
    return [...previousState, action.todo];
  }

  return previousState; // 어떠한 변화도 있지 않다면 계속 action 을 받더라도 아무 변화가 없음
};

6.createStore

  • 스토어를 만드는 함수
const store = createStore(reducer);

createStore<S>(
  reducer:Reducer<S>, // 첫번째 인자는 reducer 함수
  preloadedState:S, // initial state 를 넣을수 있는데 undefined 경우에는 reducer 에서 initial 값을 설정해서 사용함
  enhancer?: StoreEnhancer<S>
): Store<S>;

store 구조

  • store.getState(); - 현재 스토어의 state 를 가져오는 함수

  • store.dispatch(action); 또는 store.dispatch(action()); - 액션을 인자로 넣어서 store 의 상태를 변경시키는 것

  • const unsubscribe = store.subscribe(()=> {}); - store 에 변경이 생겼을 경우 subscribe 안에 함수를 실행하는 명령어 그 결과물이 unsubscribe 역활을 하는것인데

    • return 이 unsubscribe 라는 점 !

    • unsubscribe() 하면 subscribe 되는 함수를 제거

  • store.replaceReducer(다른 리듀서) - 원래 가지고 있는 reducer 를 다른 reducer 로 바꾸는 역활을 하는것

7.CombineReducers

  • redux 로 부터 import 해서 사용하는 함수 로서 redux 는 단일 스토어기 때문에 app 에 데이터가 많아지고 복잡해지면 reducer 안이 목잡하게 됨 : store 를 여러개 사용할 수 없습니다.

  • 그래서 reducer 안에 내용을 쪼개는 능력이 필요합니다. 같은 속성값의 reducer 끼리 쪼개고 그다음에 다시 함쳐서 reducer 를 사용하는 것이 combineReducer 입니다.

// combine reducer 에서 대표해서 각 reducer 를 만들어 주고 각 reducer 에서 쪼개서 사용함
import { combineReducers } from "redux";
import todos from "./todos";
import filter from "./filter";

const reducer = combineReducers({
  todos,
  filter,
});

export default reducer;

// in todos reducer

import { ADD_TODO, COMPLETE_TODO } from "../actions";

const initialState = [];

const todos = (previousState = initialState, action) => {
  // 기존 action 의 type 이 ADD_TODO 와 같을 경우 이전것과 action.todo 를 합쳐서 return 해줌
  if (action.type === ADD_TODO) {
    return [...previousState, { text: action.text, done: false }];
  }

  if (action.type === COMPLETE_TODO) {
    return previousState.map((todo, index) => {
      if (index === action.index) {
        return { ...todo, done: true };
      }
      return todo;
    });
  }

  return previousState; // 어떠한 변화도 있지 않다면 계속 action 을 받더라도 아무 변화가 없음
};

export default todos;
// store 생성 시 combineReducer 만 가져와서 사용하게 되면 됨
// in store.js

import { createStore } from "redux";
import todoApp from "./reducers/reducer";

const store = createStore(todoApp);

export default store;

8.Redux를 React에 연결하기

react-redux

  • Provider component를 제공해 줍니다.

  • connect 함수를 통해 “container”를 만들어 줍니다

    • container는 store의 state와 dispatch(액션)를 연결한 component에 props로 넣어주는 역활을 합니다.
  • 필요한 사항은?

    • 어떤 state를 어떤 props 에 연결할 것인지에 대한 정의

    • 어떤 dispatch(액션)을 어떤 props 에 연결할 것인지에 대한 정의

    • 그 props를 보낼 컴포넌트를 정의

예시) containers 를 만들어서 component 에 redux state 를 뿌려주는 것

// TodoListContainer.jsx

import { useSelector } from "react-redux";
import TodoList from "../components/TodoList";

const TodoListContainer = () => {
  const todos = useSelector((state) => state.todos);
  return <TodoList todos={todos} />;
};

export default TodoListContainer;
const TodoList = ({ todos }) => {
  return (
    <ul>
      {todos.map((todo) => {
        return <li>{todo.text}</li>;
      })}
    </ul>
  );
};

export default TodoList;

🔶 🔷 📌 🔑

Reference

Categories:

Updated:

Leave a comment