REACT

[React]Redux 시작하기

[React]Redux 시작하기

redux 이해하기

  1. redux는 props를 대신하여 사용하는 라이브러리다.
  2. 모든 컴포넌트들이 같은 값을 공유할 수 있게 만들어준다. 예를 들어 중첩 컴포넌트에서 props를 여러번 전송하지 않고 state를 직접 사용할 수 있다.
  3. state 데이터 관리가 용이하여 에러 추적 용이하다. 

redux 설치하기

터미널에서 다음 명령어로 redux 라이브러리를 설치한다.

방법1. yarn add redux react-redux

방법2. npm install redux react-redux

redux 세팅하기

1.index.js에서 redux 라이브러리의 Provider를 import 한다.
import { Provider } from 'react-redux';
2.redux를 사용할 부분에 <Provider>로 감싸준다.
ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);
3.createStore()안에 state를 저장한다.
import { createStore } from 'redux';

// 장바구니 데이터
let store = createStore(()=>{
  return [
    { id : 0, name : 'White and Black', quan : 2},
    { id : 0, name : 'Red Knit', quan : 1}
  ]
});
4.<Provider>에서 state를 props로 전송한다.
      <Provider store={store}>
        <App />
      </Provider>

 

redux 기본 사용방법

예제 만들기

장바구니 페이지를 만들어 redux를 적용해 보자.

1.cart.js 생성하여 부트스트랩에서 table UI 가져온다.
import React from 'react';
import { Table } from 'react-bootstrap';

const cart = () => {
    return (
        <div>
            <Table responsive>
                <thead>
                <tr>
                    <th>#</th>
                    <th>상품명</th>
                    <th>수량</th>
                    <th>변경</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td>1</td>
                    <td>Table cell</td>
                    <td>Table cell</td>
                    <td>Table cell</td>
                </tr>
                </tbody>
            </Table>
        </div>
    );
};

export default cart;
2.App.js에서 루트를 설정한다.
import Cart from './Cart';

      <Route path="/cart"> 
        <Cart></Cart>
      </Route>

redux 사용하기 – state 공유하는 법

Cart.js에서 다음과 같이 코드를 작성한다.

1.redux의 state 데이터를 props로 변환해주는 함수를 만들어준다.
function state를props화(state){
    return {
 
    }
}
2.state 데이터를 props로 등록한다.
function state를props화(state){ // state : store에 있던 모든 데이터
    return {
        state : state // state라는 이름의 props
    }
}
3.함수를 사용하여 export default connect()()한다.
import { connect } from 'react-redux';

export default connect(state를props화)(Cart)
//export default Cart;
4.props를 사용하여 데이터 바인딩한다.
const Cart = (props) => {
    return (
        <div>
            <Table responsive>
                <thead>
                <tr>
                    <th>#</th>
                    <th>상품명</th>
                    <th>수량</th>
                    <th>변경</th>
                </tr>
                </thead>
                <tbody>
{
                        props.state.map((a,i)=>{
                            return(
                            <tr key={i}>
                                <td>{ a.id }</td>
                                <td>{ a.name }</td>
                                <td>{ a.quan }</td>
                                <td>
                                    <button className="btn btn-primary">+</button>
                                    <button className="btn btn-danger">-</button>
                                </td>
                            </tr>

                            )
                        })
                   
                    }
                </tbody>
            </Table>
        </div>
    );
};

redux 사용하기 – state 변경하는 법

1.데이터 수정 방법을 정의한 state 만든다.

  1. 데이터의 초기값을 변수에 담아준다.
  2. 데이터 수정 방법을 정의
  3. 데이터 수정 방법이 정의된 reducer를 데이터를 변수에 넣어서 사용한다.
index.js
// state 만들기
// 1. 데이터의 초기값을 변수에 담아준다.
let 초기값 = [
  { id : 0, name : 'White and Black', quan : 2},
  { id : 1, name : 'Red Knit', quan : 1}
];

// 2. 데이터 수정 방법을 정의
function reducer(state = 초기값, 액션){
  if(액션.type === '수량증가'){

    let  copy = [...state];
    copy[0].quan++;
    return copy

  }else if(액션.type === '수량감소'){

    let  copy = [...state];
    if(copy[0].quan > 1){
      copy[0].quan--;
    }else{
      alert("최소 1개 이상이어야 합니다.")
    }
    return copy

  }else {
    return state
  }
    
}

// 3. 데이터 수정 방법이 정의된 reducer를 데이터를 변수에 넣어서 사용한다.
let store = createStore(reducer);

2.정의한 수정방법을 dispatch()를 사용하여 적용한다.

Cart.js
<button className="btn btn-primary" onClick={()=>{props.dispatch({type:'수량증가'})}}>+</button>
                        <button className="btn btn-danger" onClick={()=>{props.dispatch({type:'수량감소'})}}>-</button>

redux 사용하기 – state 여러개 사용하기

Combinereducers는 state 여러개 사용하기하기 위해서 사용하는 Hook이다.

  1. 두번째 state를 만들어준다.
  2. combineReducers를 사용하여 reducer를 모두 store에 담아준다.
  3. state 데이터를 props로 만들어주는 함수에 추가한 state를 추가한다.
index.js
import { combineReducers, createStore } from 'redux';

// 1. 데이터의 초기값을 변수에 담아준다.
let alert초기값 = true;

// 2. 데이터 수정 방법을 정의
function reducer2(state = alert초기값, 액션){
//function reducer2(state = true, 액션){
  if(액션.type === '알림창닫기'){
    state = false;
    return state;

  }
  return state;
}

// 3. 데이터 수정 방법이 정의된 reducer를 데이터를 변수에 넣어서 사용한다.
let store = createStore(combineReducers({reducer, reducer2}));
Cart.js
function state를props화(state){
    return {
        state : state.reducer,
        alert열렸니 : state.reducer2
    }
}
                {
                    props.alert열렸니 === true
                    ? 
                    <div className='my-alert-yellow'>
                        <p>지금 구매하시면 신규 할인 20%</p>
                        <button className='btn btn-danger' onClick={()=>{props.dispatch({type:'알림창닫기'})}}>닫기</button>
                    </div>
                    : null           
                }

redux 사용하기 – state 변경시 데이터 전송하는 법

key : 오브젝트 형태로 데이터를 담아서 전송하고 파라미터로 데이터를 받을 수 있다. 파라미터.key 형태로 전송받은 데이터를 사용할 수 있다.

데이터 전송

detail.js
            <button className="btn btn-danger" onClick={()=>{  
              // payload에 데이터 담아보내기
              props.dispatch({type:'항목추가', payload : { id:2, name: '새로운상품', quan:1} });
              history.push('/cart');
             }}>주문하기</button> &nbsp;

history.push : 주소창에서 /cart로 이동하면 새로고침되기 때문에 push한 데이터가 표시되지 않는다. 항목추가 후에 바로 페이지 이동하여 추가된 데이터를 확인해본다.

detail.js
            <button className="btn btn-danger" onClick={()=>{  
              props.dispatch({type:'항목추가', payload : { id:props.shoes[id].id, name: props.shoes[id].title, quan:1} });
              history.push('/cart');
             }}>주문하기</button> &nbsp;

임의의 데이터가 아닌 주문하기 버튼을 클릭한 해당 상품의 데이터를 전송하는 방법은 Detail.js 페이지에서 이미 사용되고 있는 데이터를 가지고 오면 된다.

detail.js
function reducer(state = 초기값, 액션){
  if(액션.type === '항목추가'){

    // findIndex : 배열 안에 원하는 데이터를 찾아주는 함수
    let found = state.findIndex((a)=>{return a.id === 액션.payload.id});
    if(found > 0){
      let copy = [...state];
      copy[found].quan++;
      return copy;
    }else {
      let copy = [...state];
      copy.push(액션.payload);
      return copy;
    }

  }

이미 장바구니에 있는 상품이 추가되었을 경우 테이블을 새로 만들지 않고 기존에 있던 항목에 수량만 증가시킬 경우 state 수정 방법 정의에 조건을 걸어준다.

데이터 수신

index.js
function reducer(state = 초기값, 액션){
  if(액션.type === '항목추가'){

    let copy = [...state];
    // paylaod 로 보낸 데이터 가져오기
    copy.push(액션.payload);
    return copy

  }else if...

 

상품 데이터를 전송해서 각각의 장바구니 항목 수량 변경하기

현재 두번째 상품의 +, – 버튼을 클릭하면 첫번째 상품의 수량이 변경되도록 코딩되어 있습니다. 각각의 상품의 수량을 변경하도록 상품 데이터를 전송받는 코드를 작성합니다.

Cart.js
                <tbody>
                {
                        state.reducer.map((a,i)=>{
                            return(
                            <tr key={i}>
                                <td>{ a.id }</td>
                                <td>{ a.name }</td>
                                <td>{ a.quan }</td>
                                <td>
                                    <button className="btn btn-primary" onClick={()=>{dispatch({type:'수량증가', data : a.id})}}>+</button>
                                    <button className="btn btn-danger" onClick={()=>{dispatch({type:'수량감소', data : a.id})}}>-</button>
                                </td>
                            </tr>

                            )
                        })
                   
                    }

                </tbody>
index.js
function reducer(state = 초기값, 액션){
  if(액션.type === '항목추가'){

    let copy = [...state];
    // paylaod 로 보낸 데이터 가져오기
    copy.push(액션.payload);
    console.log(copy);
    return copy

  }if(액션.type === '수량증가'){

    let  copy = [...state];
    copy[액션.data].quan++;
    return copy

  }else if(액션.type === '수량감소'){

    let  copy = [...state];
    if(copy[액션.data].quan > 1){
      copy[액션.data].quan--;
    }else{
      alert("최소 1개 이상이어야 합니다.")
    }
    return copy

  }else {
    return state
  }
    
}

 

redux 최신 사용법

useSelector와 useDispatcher를 사용하면 redux를 보다 간략한 코드로 작성할 수 있습니다. 현재는 Hook을 사용하여 코드를 작성하는 경우가 더 많습니다.

useSelector()

import { useSelector } from 'react-redux';

const Cart = (props) => {

    let state = useSelector((state) => state ); // redux 안의 모든 state

...

                {
                        state.reducer.map((a,i)=>{
                            return(
                            <tr key={i}>
                                <td>{ a.id }</td>
                                <td>{ a.name }</td>
                                <td>{ a.quan }</td>
                                <td>
                                    <button className="btn btn-primary" onClick={()=>{props.dispatch({type:'수량증가'})}}>+</button>
                                    <button className="btn btn-danger" onClick={()=>{props.dispatch({type:'수량감소'})}}>-</button>
                                </td>
                            </tr>

                            )
                        })
                   
                    }

useDispatch()

import { useDispatch, useSelector } from 'react-redux';

const Cart = (props) => {

    let state = useSelector((state) => state ); // redux 안의 모든 state
    let dispatch = useDispatch();
...

<tbody>
                {
                        state.reducer.map((a,i)=>{
                            return(
                            <tr key={i}>
                                <td>{ a.id }</td>
                                <td>{ a.name }</td>
                                <td>{ a.quan }</td>
                                <td>
                                    <button className="btn btn-primary" onClick={()=>{dispatch({type:'수량증가'})}}>+</button>
                                    <button className="btn btn-danger" onClick={()=>{dispatch({type:'수량감소'})}}>-</button>
                                </td>
                            </tr>

                            )
                        })
                   
                    }

                </tbody>

 

최신글