๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
JavaScript/React

[React] Redux ๋ž€?

by soy๋ฏธ๋‹ˆ 2021. 10. 21.

 

 

 

๐Ÿ’ก Redux

redux๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” state ๋ฅผ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— store ๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์— ์‰ฝ๊ฒŒ ์ „๋‹ฌํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ ๋‹ค. ์ฆ‰ Flux ์™€ ๊ฐ™์€ ๊ตฌ์กฐ์ด๋‹ค.

 

 

React ์ž‘์—…ํ™˜๊ฒฝ์—์„œ Redux ์‚ฌ์šฉํ•˜๊ธฐ (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜)

npm install --save redux

npm install --save react-redux

npm install --save redux-actions

 

 

 

๐Ÿ’ก ํ๋ฆ„ : VIEW -> ACTION -> (STORE) -> REDUCER -> DISPATCH(ACTION) -> STATE -> UI

 

์ถœ์ฒ˜ : https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow

 

index.js ์ž‘์„ฑ

store ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  store ๋ฅผ <Provider>์— props ๋กœ ์ „๋‹ฌํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ ๋‹ค. ๊ทธ๋ฆฌ๊ณ  App.js ์—์„œ reducers ๋ฅผ ์ž„ํฌํŠธํ•œ๋‹ค.

 

 

 

 

App.js ์—์„œ ๋ฉ”์ธ ์ฝ”๋“œ ์ž‘์„ฑ

 

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {createAction, handleActions} from 'redux-actions';
class App extends Component {
  render() {
    return (
      <div>
        <h1>Redux ํ…Œ์ŠคํŠธ</h1>
        <h1>{this.props.result}</h1>
        <button onClick={this.props.onInc}>๋”ํ•˜๊ธฐ</button>
        <button onClick={this.props.onDec}>๋นผ๊ธฐ</button>
      </div>
    );
  }
}

 

๐Ÿ’ก Store

๋ชจ๋“  ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ state๋Š” store์— ์ €์žฅ๋˜๋ฉฐ, redux์˜ ๋ชจ๋“  ๊ฒƒ์„ ์ด๊ด„ํ•œ๋‹ค.

 

 

state ํ•จ์ˆ˜ ์ž‘์„ฑ Store ์—์„œ state๋ฅผ ๊บผ๋‚ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

// App: ์ด์ „๊ฐ’๊ณผ ๋ณ€๊ฒฝ๊ฐ’์ด ๋™์ผํ•˜๋ฉด App ์œผ๋กœ ์•ˆ๊ฐ„๋‹ค.
const stateToProps = state => {
  console.log('stateToProps', state.num);
  return { result: state.num };
}

 

 

๐Ÿ’ก Dispatch

state๋ฅผ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด์„œ store์— ์‹ ํ˜ธ๋ฅผ ๋ณด๋‚ด๋ฉด ๋˜๋Š”๋ฐ ์ด ์‹ ํ˜ธ๊ฐ€ action ์ด๋‹ค. dispatch ๋ฅผ ํ†ตํ•ด action ์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. dispatch ์— action ์„ ๋„ฃ์–ด์ฃผ์ง€ ์•Š์œผ๋ฉด ( => dispatch(action) ) reducer ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

dispatch ๋™์ž‘ : action์„ action creater ๊ฐ€ ๋งŒ๋“ค์–ด์ฃผ๊ณ  action creater ๋ฅผ ๋‹ด์€ dispatcher ์—ด์ฐจ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ํ•ด๋‹น action ์— ๋Œ€ํ•œ reducer ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

 

const INC = 'INC'
const DEC = 'DEC'
const inc = createAction(INC);
const dec = createAction(DEC);
const dispatchToProps = dispatch => bindActionCreators({
// action ์„ ๋‹ด๊ณ  reducer ๋กœ ํ–ฅํ•˜๋Š” dispatch ์—ด์ฐจ
    onInc: inc,
    onDec: dec
}, dispatch)

 

 

๐Ÿ’ก Reducer (Store ์˜ Reducer)

Store ์˜ state ์™€ action ์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์„œ, ํ•ด๋‹น action ์— ๋งž๋Š” ์ž‘์—…์„ ํ•˜์—ฌ state ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค. ์ด์ „ state ์™€ action ์„ ํ†ตํ•ด์„œ ์ƒˆ๋กœ์šด state ์ƒ์„ฑํ•œ๋‹ค.

 

Reducer ์—์„œ Store ๋กœ ๋ณ€๊ฒฝ๋œ state ๊ฐ’์ด ๋ฐ˜ํ™˜๋˜๊ณ  ์ €์žฅ๋œ๋‹ค.

 

const initState = {
  num: 100
}

// dispatcher ๊ฐ€ ์ „๋‹ฌํ•œ action ์˜ ํƒ€์ž…์„ ๋ณด๊ณ  ๊ทธ์— ๋งž๋Š” ํ–‰๋™์„ ํ•œ๋‹ค.
export const reducers = handleActions({
  [INC]: (state, action) => ({num: state.num+1}),
  [DEC]: (state, action) => ({num: state.num-1})
}, initState);

// reducer ์—์„œ store ๋กœ ๊ฐ’์ด ๋ฐ˜ํ™˜๋œ๋‹ค. 
// ๋ณ€๊ฒฝ๋˜๋Š” num ๊ฐ’์ด store ์— ์ €์žฅ๋œ๋‹ค.

 

 

 

 

๐Ÿ’ก state ์™€ dispatch connect ํ•˜๊ณ  export ํ•˜๊ธฐ

connect  : State - Dispatch - App

createStore : Store - Reducer

 

connect : React ์ปจํ…Œ์ด๋„ˆ๋ฅผ Redux ์— ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

 

export default App = connect(
  stateToProps,
  dispatchToProps
)(App)

 

 

 

 

์›น ์‹คํ–‰ ํ™”๋ฉด

 

 

 

 

 

 

 

๐Ÿ’ก ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ์‹คํ–‰ํ•˜๊ธฐ (Bpp)

 

๐Ÿฅ Listener ์ด๋ž€?

Store ์•ˆ์— ์žˆ๋Š” state (์ƒํƒœ) ๊ฐ€ ๋ฐ”๋€” ๋•Œ ๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

 

๐Ÿฅ subscribe ์ด๋ž€?

dispatch ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค subscribe ์— ์ „๋‹ฌํ•œ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

 

 

 

index.js ์—์„œ listener ์™€ subscribe ํ•จ์ˆ˜ ์ž‘์„ฑํ•˜๊ธฐ

 

const store = createStore(reducers)
const listener = () => {
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
}
store.subscribe(listener)
listener()
reportWebVitals();

 

 

App.js ์ž‘์„ฑํ•˜๊ธฐ

 

import React, { Component } from 'react'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'
import { createAction, handleActions } from 'redux-actions'

class App extends Component {
  render() {
    return (
         <ConnectBpp/>
    );
  }
}

class Bpp extends Component{
  render(){
    return(
      <div>
        <h1>{this.props.result}</h1>
        <button onClick={this.props.onINC}>๋”ํ•˜๊ธฐ</button>
        <button onClick={this.props.onDEC}>๋นผ๊ธฐ</button> 
      </div>
    )
  }
}

const stateToProps = state => {
  console.log('stateProps', state.num)
  return { result: state.num}
}

const INC = 'INC'
const DEC = 'DEC'
const inc = createAction(INC)
const dec = createAction(DEC)
const dispatchToProps = dispatch => bindActionCreators({
  onINC: inc,
  onDEC: dec
}, dispatch, console.log('dispatcher'))

const initState = { num: 300 }
export const reducers = handleActions({
  [INC]: (state, action) => ( { num: state.num + 1 }),
  [DEC]: (state, action) => ({ num: state.num - 1 })
}, initState)

const ConnectBpp = connect(
  stateToProps,
  dispatchToProps
)(Bpp)

export default App

 

 

 

 

๐Ÿ’ก connect ๋Œ€์‹  Hook ์‚ฌ์šฉํ•˜๊ธฐ

 

๐Ÿฅ Hook ์ด๋ž€?

ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์ƒํƒœ๊ฐ€ ๋ณ€ํ•  ๋•Œ Hook ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•œ๋‹ค.

 

 

index.js

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

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

reportWebVitals();

 

import React, { Component } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createAction, handleActions } from 'redux-actions';

class App extends Component {
  render() {
    return (
      <div>
        <ConnectBpp />,
        <ConnectCpp />,
        <Dpp />
      </div>
    );
  }
}

class Cpp extends Component {
  render() {
    return (
      <div>
        <h1>ํ™•์ธ : {this.props.result}</h1>
      </div>
    );
  }
}

class Dpp extends Component {
  render() {
    return (
      <ConnectBpp />
    );
  }
}

class Bpp extends Component {
  render() {
    return (
      <div>
        <h1>Redux ํ…Œ์ŠคํŠธ</h1>
        <h1>{this.props.result}</h1>
        <button onClick={this.props.onINC}>๋”ํ•˜๊ธฐ</button>
        <button onClick={this.props.onDEC}>๋นผ๊ธฐ</button>
      </div>
    );
  }
}

 

const INC = 'INC'
const DEC = 'DEC'
const inc = createAction(INC);
const dec = createAction(DEC);

const initState = {
  num: 100
}

export const reducers = handleActions({
  [INC]: (state, action) => ({ num: state.num + 1 }),
  [DEC]: (state, action) => ({ num: state.num - 1 })
}, initState);

const ConnectBpp = () => {
  const num = useSelector(state => state.num)
  const dispatch = useDispatch()
  return (
    <Bpp result={num}
      onINC={() => dispatch(inc())}
      onDEC={() => dispatch(dec())}
    />
  )
}

const ConnectCpp = () => {
  const num = useSelector(state => state.num)
  return (
    <Cpp result={num} />
  )
}

export default App

 

 

useSelector : connect ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  redux ์˜ state ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

useDispatch : ์ƒ์„ฑํ•œ action ์„ useDispatch ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

 

 

 

์›น ์‹คํ–‰ ํ™”๋ฉด

 

 

 

 

 

 

๋Œ“๊ธ€