React

Redux

코딩 화이팅 2024. 12. 7. 14:22

장바구니 페이지 만들기

<Route path="/cart" element={ <Cart/> } />
  • <Route>를 사용하여 페이지를 하나 만들어주기
  • Cart.js 라는 다른 파일에 컴포넌트 만들기
  • <Cart> 컴포넌트를 element 안에 넣어서 /cart로 접속하면 <Cart> 컴포넌트를 보여주기

장바구니 페이지에서 사용할 Table 레이아웃

<Table>
  <thead>
    <tr>
      <th>#</th>
      <th>상품명</th>
      <th>수량</th>
      <th>변경하기</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>안녕</td>
      <td>안녕</td>
      <td>안녕</td>
    </tr>
  </tbody>
</Table>
  • 이렇게 넣으면 표가 생성(React-bootstrap)

Redux

  • props 없이 state를 공유할 수 있게 도와주는 라이브러리
  • redux를 설치하면 js 파일 하나에 state들을 보관할 수 있다
  • 이걸 모든 컴포넌트가 직접 꺼내쓸 수 있다.
    -> props 전송이 필요없어짐(컴포넌트가 많을수록 좋다)
  • 사이트가 커질 때 쓰면 좋다

Redux 설치

npm install @reduxjs/toolkit@1.8.1 react-redux
  • 터미널에 입력
  • package.json 파일을 열어 "react", "react-dom" 항목의 버전을 확인하여 두개가 18.1.x 이상이면 사용 가능

  • 그게 아니라면 직접 두개를 18.1.0 이렇게 수정한 후 파일 저장하고 터미널에 npm install 하면된다. 그럼 redux 설치 가능

Redux 세팅

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
})

=====================================================================

import { Provider } from "react-redux";
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>
);
  1. 아무데나 store.js 파일을 만들어서 위 코드를 복붙(src 폴더 안에)
    state들을 보관하는 파일들
  2. index.js 파일가서 Provider라는 컴포넌트와 아까 작성한 파일을 import하기
  3. 밑에 <Provider store={import해온거}> 이걸로 <App/>을 감싸면 된다.
  4. 그럼 이제 <App>과 그 모든 자식 컴포넌트들은 store.js에 있던 state를 맘대로 꺼내쓸 수 있다.

Redux store에 state 보관하는 법

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

let user = createSlice({
  name : 'user',
  initialState : 'kim'
})

export default configureStore({
  reducer: {
    user : user.reducer
  }
})
  1. createSlice() 상단에서 import
  2. {name : 'state이름', initialState:'state값'} 이렇게 넣으면 state 하나 생성
    createSlice()는 useState()와 용도가 비슷
  3. state 등록은 configureStore() 안에 하면 된다.
  4. {작명 : createSlice 만든거.reducer) 하여 등록
    여기 등록한 state는 모든 컴포넌트가 자유롭게 사용 가능

Redux store에 있던 state 가져다쓰는 법

(Cart.js)

import { useSelector } from "react-redux"

function Cart(){
  let a = useSelector((state) => { return state } )
  console.log(a)

  return (생략)
}
  • 아무 컴포넌트에서 useSelector((state)=>{return state}) 쓰면 store에 있던 모든 state가 그 자리에 남는다.
let a = useSelector((state) => state.user )
  • 이런 식으로 써서 store에서 필요한 데이터만 가져다 쓸 수 있다.
  • 간단한거 만들 때나 컴포넌트가 몇개 없을 때는 그냥 props 쓰는게 더 코드가 짧다.

Redux state가 array / object인 경우 변경하려면?

let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    changeName(state){
      state.name = 'park'
    }
  }
})
  • {name : 'kim', age : 20} 이렇게 생긴 자료에서 'kim'=>'park' 이렇게 변경하고싶다면?
  • 굳이 return 적고 뒤에 수정하고 싶은 새로운 array / object 값을 안 적어주고 state를 직접 수정하라고해도 변경이 된다.
    =>Immer.js 라는 라이브러리가 state 사본을 하나 더 생성해주기 때문에
  • 일부러 state 만들 때 문자나 숫자 하나만 필요해도 일부러 object 아니면 array에 담는 경우도 있다 -> 수정하기 편해서

간단 응용

버튼을 하나 만들고 버튼을 눌렀을 때 state 중에 age 항목이 +1 되게 하려면?

let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    increase(state){
      state.age += 1
    }
  }
})
  • 이렇게 increase 함수 만들고 export하고 필요한 곳에서 import해서 dispatch(increase()) 하면 된다.

state 변경 함수가 여러개 필요하다면?

let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    increase(state, action){
      state.age += action.payload
    }
  }
})
  • 만약 +1이 아닌 다양한 숫자를 더할 수 있게 하고싶다면 이런 식으로 파라미터 입력을 해서 함수 사용이 가능
  • 파라미터 자리에 넣은 자료들은 a.payload 하면 나온다.
  • action.type 하면 state 변경함수 이름이 나오고
  • action.payload 하면 파라미터가 나온다.

파일 분할

  • store.js 안에 코드가 점점 길어진다면 코드 덩어리들을 다른 파일로 빼면 됨.
  • 파일을 새로 만들고 let user 변수와 state 변경 함수 export 부분을 새로운 파일에 복붙하면 된다.
  • 그 후 import export 하여 다른 컴포넌트에서도 사용가능하게 할 수 있음

응용 : 수량+1 기능

let cart = createSlice({
  name : 'cart',
  initialState : [
    {id : 0, name : 'White and Black', count : 2},
    {id : 2, name : 'Grey Yordan', count : 1}
  ],
  reducers : {
    addCount(state, action){
      state[action.payload].count++
    }
  }
})
  • addCount 라는 함수를 만든다.
  • dispatch(addCount(0)) 하면 0번째 상품이 +1 되고 dispatch(addCount(1)) 하면 1번째 상품이 +1된다.
  • export 해놓고 필요할 때 쓰면 된다.
(Cart.js)

<tbody>
  {
    state.cart.map((a, i)=>
      <tr key={i}>
        <td>{state.cart[i].id}</td>
        <td>{state.cart[i].name}</td>
        <td>{state.cart[i].count}</td>
        <td>
          <button onClick={()=>{ dispatch(addCount(i)) }}>+</button>
        </td>
      </tr>
     )
   }
</tbody>
  • +버튼을 만들고 addCount() 하라고 코드 작성
  • import를 먼저 하고 addCount(i)라고 변수를 넣어주면 0번째 눌렀을 때 0번 상품을 1번째 눌렀을 땐 첫번째 상품을 실행해준다.

더 정확하게 하려면?

버튼 누르면 버튼 옆에 있는 상품 id 가져와서 이거랑 같은 id를 가진 상품을 state에서 찾아서 그걸 +1 하는게 더 정확할 거 같다.

dispatch(addCount(state.cart[i].id))
  • 일단 버튼을 누르면 옆에 있던 상품 id를 payload로 전송하고
let cart = createSlice({
  name : 'cart',
  initialState : [
    {id : 0, name : 'White and Black', count : 2},
    {id : 2, name : 'Grey Yordan', count : 1}
  ],
  reducers : {
    addCount(state, action){
      let 번호 = state.findIndex((a)=>{ return a.id === action.payload })
      state[번호].count++
    }
  }
})
  • payload와 같은 id를 가진 상품을 찾아서 1+1 해달라고 코드 작성
  • array 자료에서 원하는 항목을 찾으려면 반복문, find(), findIndex() 이것 중에 사용하면 된다.
    • findIndex()는 array 뒤에 붙일 수 있다
    • 안에 콜백함수 넣고 return 뒤에 조건식
    • a라는 파라미터는 array 안에 있던 하나하나의 자료
    • array에 있던 자료를 다 꺼내서 조건식에 대입해보는데 조건식이 참이면 그게 몇번째 자료인지 알려줌