티스토리 뷰

Library | Framework/React JS

[React] Redux

공부하는 승승 2022. 11. 7. 23:11

Redux를 사용하면 컴포넌트들이 props없이 state 공유가능

 

Redux 설치

❗ package.json에서 react와 react-dom의 버전이 18.1.0이상만 가능!

 

  • 터미널에서 하던작업 멈추고( Ctrl+c)
      npm install @reduxjs/toolkit react-redux  

 

Redux 세팅

1. src폴더 안에 store.js 파일 생성 - state를 보관할 곳

    - store.js 파일 안에 아래 코드 작성

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
})

 

2. index.js에서 아래 코드 작성

<Provider store={store}>

- 전체 코드

import store from './store.js'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  // </React.StrictMode>
);

 

-> 여기까지 세팅하면 이제 App 파일과 그 자식들은  store.js에 있는 state 전부 사용 가능

 

 


state 사용하기

  • Redux store에 state 보관하는 방법
    1. creacteSlice를 사용해서 변수 지정, 키와 값 지정
    2. reducer 안에 사용할 변수 넣기
import { configureStore, createSlice } from '@reduxjs/toolkit'

/* Redux store에 state 보관하는 방법 */
let user = createSlice({ // useState 역할
  name: 'user',
  initialState : 'Kim'
}) 
let stock = createSlice({ // useState 역할
  name: 'stock',
  initialState :[
    {id : 0, name : 'White and Black', count : 2},
    {id : 2, name : 'Grey Yordan', count : 1}
  ] 
}) 

/* ! Redux store 안에 모두 넣진 말기. 컴포넌트 간 공유가 필요없으면 useState() 사용 */
export default configureStore({
  reducer: { 
    user : user.reducer,
    stock : stock.reducer
  }
})

 

  • Redux store에서 state 가져오는 방법
function Cart(){

  /* Redux store에서 state 가져옴 */
  // let state = useSelector((state) => { return state }) // return하는 것만 가져다 씀
  let state = useSelector((state) => state ) // 중괄호 return은 축약가능
  console.log(state) // ex) return state.stock이면 state = stock 값만 나옴

  return (
    {state.stock[0].name} {/* store에서 가져온 값 바인딩 */}
  )
}

export default Cart

 

 


state 변경하기

  1. state를 수정하는 함수를 만듦
  2. 수정이 필요할 때 해당 함수를 실행하달라고 store.js에 요청- store.js에 1번에서 만든 함수 export
  3. 만든 함수가 필요한 곳에서 store.js로 요청을 보내주는 함수 useDispatch()로 변수를 만들어줌
    - 만든 함수가 필요한 곳에 3번에서만든변수명(state변경함수())

 

- store.js

let user = createSlice({
  name : 'user',
  initialState : 'Kim',
  reducers : { // state 변경 1. 변경함수 만들기
    changeName(state){  // 여기에 export를 붙일 수는 없음 -> 밖으로 빼기
      return 'John' + state
    }
  }
})
export let { changeName } = user.actions // 2. export 해주기. 위에서 만든 변경함수가 object 자료형식으로 남음
// 여기서 user.actions의 user는 1번줄의 user

* action = state 변경함수

 

 

- Cart.js

import { changeName } from '../store';

function Cart(){

  let state = useSelector((state) => state )
  let dispatch = useDispatch() // store.js로 요청을 보내주는 함수

  return (
   <div>
     { state.user }의 장바구니
     <button onClick={() => {
       dispatch(changeName()) // dispatch(state변경함수()
     }}>+</button>                 
   </div>
  )
}

export default Cart

 

 

state 변경함수의 장점

  • 버그 발생시 원인파악이 쉬움( 컴포넌트가 HTML을 수정하면  모든 컴포넌트를 다 뒤져야함)

 


state가 array/object인 경우

  • return { 키 : '값' }  적어서 직접 값을 갈아끼우거나
  • state.키 = '값' 적어서 해당 키값만 변경(immer.js 라이브러리가 알아서 해줌)
let user = createSlice({
  name : 'user',
  initialState : { name : 'Kim', age : 20 }, // Q. state가 array/object인 경우 변경방법은?
  reducers : { 
    changeName(state){  
      // return { name : 'Park', age : 20 }
      state.name = 'Park'
    }
  }
})
export let { changeName } = user.actions

 

 

state 변경함수에 파라미터 넣기

 

- store.js

let stock = createSlice({
  name : 'stock',
  initialState :[
    {id : 0, name : 'White and Black', count : 2},
    {id : 2, name : 'Grey Yordan', count : 1}
  ],
  reducers : {
    increase(state){
      state[0].count += 1
    },
    increase2(state, a){
      state[1].count += a.payload // payload를 붙여야 해당 함수의 파라미터값이 제대로 들어감
    }
  }
})

export let { increase, increase2 } = stock.actions

* dispatch(increase2(10)) = dispatch가 increase2란 메세지에 10이란 화물도 같이 보낸다는 의미 

 

- Cart.js

import { changeName, increase, increase2 } from '../store';

function Cart(){

  let state = useSelector((state) => state )
  let dispatch = useDispatch() // store.js로 요청을 보내주는 함수

  return (
    <div>
      <button onClick={() => {
         dispatch(increase2(10))
      }}>+10버튼</button>
    </div>
  )
}

export default Cart

 

 

 


코드가 길어질 때 코드 분리하기

 

(코드 분리 전)

- store.js

import { configureStore, createSlice } from '@reduxjs/toolkit'

let user = createSlice({
  name : 'user',
  initialState : 'Kim',
  reducers : {
    changeName(state){
      return 'John' + state
    }
  }
})
export let { changeName } = user.actions

 

- Cart.js

import { changeName, increase, increase2 } from './../store';

 

 

(코드 분리 후)

-store.js 

import { configureStore, createSlice } from '@reduxjs/toolkit'
import user from './store/userSlice.js'

 

- userSlice.js

import { createSlice } from '@reduxjs/toolkit'

let user = createSlice({
  name : 'user',
  initialState : { name : 'Kim', age : 20 },
  reducers : { 
    changeName(state){  
      // return { name : 'Park', age : 20 }
      state.name = 'Park'
    }
  }
})
export let { changeName } = user.actions

export default user

 

- Cart.js

import { changeName } from './../store/userSlice';
import { increase, increase2 } from './../store';

 

 


📘 장바구니 > 추가 버튼 클릭 시 상품 수량 증가

  • 상품 추가 버튼을 눌렀을 때 해당 상품의 수량이 증가해야함
  • 새로 정렬 됐을 경우(ex) 오름차순 내림차순 가격순 이름순...) index 번호가 바뀌기 때문에 클릭한 상품id와 상품data에 있는 상품 id가 동일한 경우 해당 상품의 수량이 증가하도록 해야함 

 

- Cart.js

import { Table } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { increase3 } from './../store';

function Cart(){
  let state = useSelector((state) => state )
  let dispatch = useDispatch()
  return (
    <div>
      <Table>
        <thead>
          <tr>
            <th>#</th>
            <th>상품명</th>
            <th>수량</th>
            <th>변경하기</th>
          </tr>
        </thead>
        <tbody>
          {
            state.stock.map((a, i) => {
              return (
                <tr key={i}>
                  <td>{state.stock[i].id}</td>
                  <td>{state.stock[i].name}</td>
                  <td>{state.stock[i].count}</td>
                  <td>
                    <button onClick={() => {
                      const stockId = state.stock[i].id
                      dispatch(increase3(stockId))
                    }}>+</button>
                  </td>
                </tr>
              )
            })
          }
        </tbody>
      </Table> 
    </div>
  )
}

export default Cart

 

- store.js

import { configureStore, createSlice } from '@reduxjs/toolkit'
import user from './store/userSlice.js'

let stock = createSlice({
  name : 'stock',
  initialState :[
    {id : 0, name : 'White and Black', count : 2},
    {id : 2, name : 'Grey Yordan', count : 1}
  ],
  reducers : {
    increase3(state, action){
      let stockId = state.findIndex((data) => { return data.id === action.payload })
      state[stockId].count++
    }
  }
})

export let { increase3 } = stock.actions

export default configureStore({
  reducer: { 
    user : user.reducer,
    stock : stock.reducer
  }
})

 

 

 

 

 

 

 

 

 

 

'Library | Framework > React JS' 카테고리의 다른 글

[React] React-Query  (0) 2022.11.10
[React] localStorage  (0) 2022.11.09
[React] Context API  (0) 2022.11.05
[React] AJAX, 서버와 통신  (0) 2022.11.04
[React] Lifecycle, useEffect  (0) 2022.11.03
댓글