Programming/React

[React] 상태관리 Redux

RosyPark 2021. 1. 14. 14:03

Redux 

Redux와 Redux가 없는것 비교 

 

- React 생태계에서 가장 사용률이 높은 상태관리 라이브러리(다른 상태관리 Redux, MobX) 

- React만 사용해도 애플리케이션을 개발할 수 있지만 규모가 커지고 구조가 조금이라도 복잡해지면 컴포넌트의 props나  State를  관리하는게 어려움 -> 그래서 상태 관리 라이브러리 redux를 사용하여 상태를 단순하게 처리함 

- Store안에 State가 저장함, store에는 reducer가 반드시 있어야 함

- Reducer(Handle Action, change state = 변화를 일으키는 함수 ) 는 state와 action을 받아 적절한 로직을 처리하여 새로운 state를 반환하고 그 값을 반영하여 새롭게 render를 그리도록함   

 


Redux Data Flow

- 엄격한 unidirectional data flow를 가짐 

- 아래에 있는 Redux Flow도 좋으나 API(Application Programming Interface)  요청, 비동기 작업과 관련된 상태 관리를 위해 미들웨어를 적용하면서 Redux MiddleWar 도입필요 -> 관련 내용 

Store

- 리덕스에서는 한 애플리케이션 당 하나의 스토어를 만들게 됨

- 스토어 안에는 현재 앱 상태, Reducer, 추가적으로 내장함수(ex, Dispatch, Subscribe ) 가 존재 

 

Dispatch

- Action을 발생시키는 것(Action을 parameter로 전달 ex:dispatch(action)) 

- Store의 내장함수 중 하나

 

Subscribe

- Store의 내장함수 중 하나 

- 함수 형태의 값을 파라미터로 받아옴

- Subsribe함수에 특정 함수를 전달해주면, Action이 dispatch 되었을 때마다 전달해준 함수가 호출 

  

 


Redux 규칙

1. 하나의 애플리케이션 안에는 하나의 스토어가 있음 

2. 상태는 읽기전용

3. 변화를 일으키는 함수, 리듀서는 순수한 함수여야 함 

- 리듀서 함수는 이전 상태와, 액션 객체를 파라미터로 받음

- 이전의 상태는 절대로 건들이지 않고, 변화를 일으킨 새로운 상태 객체를 만들어서 반환

- 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과값을 반환

=> 하지만 동일한 인풋 -> 동일한 아웃풋이 아닌 작업의 경우? 순수하지 않은 작업이기 때문에 리듀서 함수의 바깥에서 처리해줘야함 -> 리덕스 미들웨어 사용 -> rosypark.tistory.com/430

                                                                                                                                                                 

 

step1) 기본적인 Redux 

import {createStore} from 'redux';
import { Provider} from 'react-redux';
import reducers from './reducers';

const store = createStore(reducers)

ReactDOM.render(
  <Provider store={store}>
        <App />
    </Provider>,
  document.getElementById('root')
);

 

 

step2)  개발자 도구 더하기 

import { createStore } from 'redux'
import reducers from './reducers';
import { Provider } from 'react-redux';

const store = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
);

 

 

 

step3)  Redux를 잘 쓸 수 있게 해주는 middle ware 더하기 

- 리덕스를 사용하면서 비동기작업(네트워크 요청)을 다룰때에는 미들웨어가 있어야지 더욱 손쉽게 상태를 관ㄱ리할 수 있음. dispatch() 메소드를 사용 

 

여러가지 MiddleWare을 적용하는 방법 

import {applyMiddleware, createStore} from 'redux';
import {Provider} from 'react-redux';
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-think';
import Reducer from './_reducers/index.';

const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk) (createStore)

ReactDOM.render(
  <Provider 
      store = {createStoreWithMiddleware(Reducer,
               window.__REDUX_DEVTOOLS_EXTENSION__ &&
               window.__REDUX_DEVTOOLS_EXTENSION__()
      )} > 
    <App />
  </Provider>,
  document.getElementById('root')
);

 

 

 

 

 

 

 

<참고> 

Props와 State

(1) Props?

- 컴포넌트 내부의 Immutable Data

- 외부(부모) 컴포넌트에게 받은 속성 

- JSX 내부에 {this.props.propsName}

- 컴포넌트를 사용할 때, <> 괄호안에 propsName = "value"

- this.props.children은 기본적으로 갖고 있는 props 

 

(2) State 

- mutable한 데이터

- 독립적인 컴포넌트의 상태 

- JSX 내부에 {this.state.stateName}

- this.state = {} 설정 

 

NextRedux 

 

 

 

 

 

 

 

 


Example 1 -  일반 React  Redux 

npx create-react-app .
npm install redux react-redux

 

 

index.js 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {createStore} from 'redux'; 



//ACTION Increment 
const increment = () => {
  return {
    type : 'INCREMENT'
  }
}

const decrement = () => {
  return {
    type : 'DECREMENT'
  }
}

//Reducer 
const counter = (state = 0, action ) => {
  switch(action.type){
    case 'INCREMENT' : 
      return state + 1 ;
    case 'DECREMENT':
      return state - 1;
  }
}

//STORE -> Globalized State
let store = createStore(counter);


//Display it in the console
store.subscribe(()=> console.log(store.getState()));

//Dispatch = Action 
store.dispatch(increment());
store.dispatch(increment());
store.dispatch(decrement());


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();

 

 


Example 2 -  일반 React  Redux 

Example

 

App.js

index.js 

actions

 - index.js 

reducers

 - counter.js

 - index.js

 - isLogged.js 

 

 

 

 

App.js

import './App.css';
import {useSelector, useDispatch} from 'react-redux';
import {increment, decrement} from './actions';

function App() {
  const counter = useSelector(state => state.counter);
  const isLogged = useSelector(state => state.isLogged);
  const dispatch = useDispatch();

  return (
    <div className="App">
      <h1> Counter {counter} </h1>
      <button onClick = {()=> dispatch(increment())}> + </button>
      <button onClick = {()=> dispatch(decrement())}> - </button>
      
      {isLogged?  <h3> Valuable Information I shouldn't see</h3> : ''}
    </div>
  );
}

export default App;

 

index.js 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {createStore} from 'redux'; 
import allReducer from './reducers';
import {Provider} from 'react-redux';


const store = createStore(
  allReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);


ReactDOM.render(
 <Provider store = {store}> 
   <App />
 </Provider>,
  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();

- React에서 Redux는 페이지는 Provider로 감싸준다. 

 

 

reducers - counter.js

const counterReducer = (state = 0, action ) => {
    switch(action.type){
        case 'INCREMENT'  :
            return state + action.payload;
        case 'DECREMENT' : 
            return state - 1 ;
        default:
            return state;

    }
}
export default counterReducer; 

 

reducers - index.js

import counterReducer from './counter';
import loggedReducer from './isLogged';
import {combineReducers} from 'redux'; 

const allReducers = combineReducers({
    counter : counterReducer, 
    isLogged : loggedReducer
});

export default allReducers;

 

reducers - isLogged.js 

const loggedReducer = (state= false,  action) => {
    switch(action.type){
        case 'SIGN_IN':
            return !state;
        default:
            return state;
        
    }

};

export default loggedReducer;

 

 

actions - index.js 

export const increment = (number) => {
    return {
        type : 'INCREMENT',
        payload : number
    };
};

export const decrement = () => {
    return {
        type : 'DECREMENT'
    };
};

 

 

 


Redux dev Tool Extension

1. 바로 설치

npm install redux-devtools-extension

 

2. Googld DevTools 확장 

- 관련 github 

chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd

 

Redux DevTools

Redux DevTools for debugging application's state changes.

chrome.google.com

 

 

 

 

 

 

 

 

<출처>

1. react.vlpt.us/using-typescript/

 

8장. 리액트 프로젝트에서 타입스크립트 사용하기 · GitBook

8장. 리액트 프로젝트에서 타입스크립트 사용하기 8장에서는 리액트 프로젝트에서 타입스크립트를 사용하는 방법에 대하여 알아보도록 하겠습니다. JavaScript 는 weakly typed 언어이기에, 특정 숫자

react.vlpt.us

2.d2.naver.com/helloworld/1848131

3. velopert.com/3401

4. medium.com/humanscape-tech/redux%EC%99%80-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-thunk-saga-43bb012503e4

5.