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>
);
- 아무데나 store.js 파일을 만들어서 위 코드를 복붙(src 폴더 안에)
state들을 보관하는 파일들 - index.js 파일가서 Provider라는 컴포넌트와 아까 작성한 파일을 import하기
- 밑에 <Provider store={import해온거}> 이걸로 <App/>을 감싸면 된다.
- 그럼 이제 <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
}
})
- createSlice() 상단에서 import
- {name : 'state이름', initialState:'state값'} 이렇게 넣으면 state 하나 생성
createSlice()는 useState()와 용도가 비슷 - state 등록은 configureStore() 안에 하면 된다.
- {작명 : 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에 있던 자료를 다 꺼내서 조건식에 대입해보는데 조건식이 참이면 그게 몇번째 자료인지 알려줌