React-Redux #2

정지훈·2021년 4월 29일
0

Action

Action을 하기 전에 state가 구상되어 있어야 한다.

리덕스의 액션이란?

  • 액션은 사실 그냥 객체 입니다. (플레인 객체)
  • 두 가지 형태의 액션이 있습니다.
    • {type:'TEST'} // payload없는 액션
    • {type: 'TEST', params: 'hello'} // payload 있는 액션
  • tyoe만이 필수 프로퍼티이며 type은 문자열이다.

문자열의 가장 큰 단점은 오타이다.

리덕스의 액션 생성자란?

	function 액션생성자(...args) {return 액션;}
  • 액션을 생성하는 함수를 "액션 생성자 {Action Creator} 라고 합니다. 액션을 리턴하는 함수이다.
  • 함수를 통해 액션을 생성해서 액션 객체를 리턴해준다.
  • createTest('hello') // {type:'TEST', oarans: 'hello'} 리턴

리덕스의 액션은 어떤 일을 하나?

  • 액션 생성자를 통해 액션을 만들어 낸다.
  • 만들어낸 액션 객체를 리덕스 스토어에 보낸다.

초록색이 store로 날라간게 액션 객체이다.
- 리덕스 스토어가 액션 객체를 받으면 스토어의 상태값이 변경된다.

액션을 보냈으면 파란색 아이들이 다 바꾸려고 랜더할려고 한다. 랜더를 하려고 하고 싶으면 액션을 만들어서 보내야 한다.

  • 변경된 상태 값에 의해 상태를 이용하고 있는 컴포넌트가 변경된다.
  • 액션은 스토어에 보내는 일종의 인풋이라 생각할 수 있다.

액션을 준비하기 위해서는?

  • 액션의 타입을 정의하여 변수로 빼는 단계
    • 강제는 아니다.
    • 그냥 타입을 문자열로 넣기에는 실수를 유발할 가능성이 크다.
    • 미리 정의한 변수를 사용하면 스펠링에 주의를 덜 기울여도 된다.
  • 액션 객체를 만들어 내는 함수를 만드는 단계 (actioncreator를 만든다.)
    • 하나의 액션 객체를 만들기 위해 하나의 함수를 만들어 낸다.
    • 액션의 타입은 미리 정의한 타입 변수로 부터 가져와서 사용한다.

액션 준비 코드

// actions.js

// 액션의 type 정의
// 액션의 타입 => 액션 생성자 이름
// ADD_TODO => addTodo
export const ADD_TODO = 'ADD_TODO';

// 액션 생산자
// 액션의 타입은 미리 정의한 타입으로 부터 가져와서 사용하며,
// 사용자가 인자로 주지 않습니다.

export function addTodo(text) {
  return { type: ADD_TODO, text };
} // { type: ADD_TODO, text: text }

actions.js를 만들자.

action을 만들기 전에 state를 구상해보자. 어떤 state를 줄지 생각해보자. ["장보기", "산책하기"]; 이런 텍스트들이 ADD_TODO라는 아이를 액션을 실행할 때 같이 들어가야 하는 단어기 때문에 이런 아이들이 있는 경우에는 페이로드를 넣어줘야한다. (페이로드란 화물이 도착하면 짐을 넣고 보내는 걸로 이해)

ADD_TODO할 때 장보기를 넣고 실행하면 type이 ADD_TODO이고 text가 장보기인 액션 객체가 만들어 진다.

Reducers - 리듀서

리덕스의 리듀서란?

  • 액션을 주면, 그 액션이 적용되어 달라진(안 달라질수도..) 결과를 만들어 줌
  • 그냥 함수이다.
    • Pure Function (순수 함수)
    • Immutable
      • 왜 ?
        • 리듀서를 통해 스테이트가 달라졌음을 리덕스가 인지 하는 방식

순수함수는 비순수함수와 다르게 부수효과가 일어나지 않고 같은 인풋을 넣으면 같은 결과를 만들어 주는 순수함수이다.
그리고 Immutable이다. 리듀서를 통해서 스테이트가 달라졌음을 리덕스가 인지를 한다.

function 리듀서(previousState, action) {
  return newState;
}
  • 액션을 받아서 스테이트를 리턴하는 구조
  • 인자로 들어오는 previousState와 리턴되는 newState는 다른 참조를 가지도록 해야합니다.

previousState: 이전의 상태값
action: 현재 들어온 액션

그림에서 보면 보라색이 액션을 던지면 store안에서 reducer함수가 실행된다. 어떻게 실행 되나? 초록색을 받았을 때의 현재 상태와 초록색 액션을 인자로 해서 바뀔 스테이트를 리턴해 내는 그런 함수를 실행하는 것 이다.

그래서 첫 번째 인자가 현재 스테이트 2번째는 방금 받아들인 액션 리턴이 새로운 스테이트이다.

받은 previousState를 새로 만들어서 보내야한다. setState할 때와 같은 방식이다.

  • reducers.js
// 언제 실행 되나?
// 1. 앱이 최초로 실행될 때 => 초기 state를 만들어서 할당한다. 이런 행동을 해야한다.
// 2. 액션이 날라왔을 때
function todoApp(previousState, action) { 
  // 앱이 최초로 실행됬을 때 타이밍을 알려면 최초에 previousState는 undefined가 들어온다.
  // 최초에 초기값 할당
  if (previousState === undefined) {
    return []; // 초기값
  }
  
  // 변경이 일어나는 로직
  
  // 변경이 안일어났을때
  return previousState;
}

변경이 일어날려면 밑에와 같이 해야한다.

import { ADD_TODO } from "./actions";

// 언제 실행 되나?
// 1. 앱이 최초로 실행될 때 => 초기 state를 만들어서 할당한다. 이런 행동을 해야한다.
// 2. 액션이 날라왔을 때
function todoApp(previousState, action) { 
  // 앱이 최초로 실행됬을 때 타이밍을 알려면 최초에 previousState는 undefined가 들어온다.
  // 최초에 초기값 할당
  if (previousState === undefined) {
    return []; // 초기값
  }
  
  // 변경이 일어나는 로직
  if (action.type === ADD_TODO) {
    return [...previousState, action.text];
  }
  
  // 변경이 안일어났을때
  return previousState;
}

정리해 보면 todoApp을 실행하는데 현재값과 새로운 액션이 들어왔다. 근데 만약에 undefined면 최초이기 때문에 그때 state값을 빈배열로 초기값으로 할당하고 만약 다른 액션이 들어오거나 다른 일이 일어나면은 반응을 안한다.

  if (action.type === ADD_TODO) {
    return [...previousState, action.text];
  }

이거에만 반응할 일으키꺼니까 ADD_TODO라는 타입에 액션을 발행한 것이기 때문에 그것에 변경하는 로직을 추가해 주고 이거 외에는 변경 안한다고 한다.

이때까지 놀라운건 지금까지 import redux를 한적이 없다.

액션에서 addTodo를 쓴 적이 없다. 지금까지 한번도 발생시킨적이 없기 때문이다. reducer를 만들때 발생하는게 아니라 누가 발생시키는 건가? 그것은 리엑트 컴포넌트가 클릭을 했을때 addTodo를 넣는다 이런 식이다.

createStore

store가 중요하다. redux로 부터 import를 하는 함수이다.

const store = createStore(리듀서);

우리가 만든 리유서인 todoApp이라는 리듀서이다. 그래서 todoApp이라는 리듀서를 인자로 넣고 createStore를 실행하면 그 결과로 Store가 나온다.

  • createStore 아이는 이런 타입을 가지고 있다.

    • 첫번째 인자로 reducer: Reducer를 받고
    • 두번째 인자로 preloadedState를 받고
    • 세번째 인자로 enhancer?: StoreEnhancer을 받는다.

    지금은 두번째인자 세번째 인자는 사용을 안하고 Reducer만 가지고 실행을 할 것이다.

    enhancer는 나중에 자세히 공부 할 것이다.

    두번째 인자는 최초에 previousState가 undefined인데 2번째 인자를 뭔가를 넣으면 previousState가 undefined가 아니게 된다.

스토어 만들기

src에 store.js를 만들자.

  • store.js
import { createStore } from 'redux';

const store = createStore()

해서 createStore에 첫 번째 인자로 우리가 만든 리듀서를 넣는다. reducers.js에서 export해서 사용해 보자.

  • reducers.js
import { ADD_TODO } from "./actions";

// 언제 실행 되나?
// 1. 앱이 최초로 실행될 때 => 초기 state를 만들어서 할당한다. 이런 행동을 해야한다.
// 2. 액션이 날라왔을 때
export function todoApp(previousState, action) { 
  // 앱이 최초로 실행됬을 때 타이밍을 알려면 최초에 previousState는 undefined가 들어온다.
  // 최초에 초기값 할당
  if (previousState === undefined) {
    return []; // 초기값
  }
  
  // 변경이 일어나는 로직
  if (action.type === ADD_TODO) {
    return [...previousState, action.text];
  }
  
  // 변경이 안일어났을때
  return previousState;
}

store.js

import { createStore } from 'redux';
import {todoApp} from './reducers';

const store = createStore(todoApp)

export default store;

이렇게 만들자.

store를 가져다 써보자 .

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 store from './store';

console.log(store);

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

콘솔에는 찍히는건 아래와 같다.

함수이름이 dispatch getState replaceReducer subscribe 4개밖에 없다.

redux라는 라이브러리는 엄청 일을 하는 라이브러리이다.

0개의 댓글