Redux는 React에서 props 없이 컴포넌트 간 상태를 공유할 수 있도록 도와주는 라이브러리입니다.
하지만 Redux는 설정이 복잡하고, 코드가 반복되며, 불변성을 유지하기 어렵다는 단점이 있었습니다. 이를 해결하기 위해 React Redux와 Redux Toolkit이 등장했습니다.
createStore
: Redux 스토어 생성subscribe
: 스토어 상태 변화를 감지getState
: 현재 상태 가져오기dispatch
: 액션을 스토어에 전달connect
: 클래스형 컴포넌트에서 Redux 상태/액션 연결useDispatch
: 함수형 컴포넌트에서 액션 전달useSelector
: 함수형 컴포넌트에서 상태 가져오기configureStore
: Redux 스토어 설정 및 생성createSlice
: state와 reducer를 함께 생성createAsyncThunk
: 비동기 액션을 쉽게 생성Redux Toolkit은 기존 Redux의 단점을 보완하기 위해 만들어졌습니다.
1. 설정 간소화: 복잡한 설정을 최소화
2. 미들웨어 설치 간편화: 기본적으로 Redux Thunk 포함
3. 코드 중복 감소: createSlice
로 reducer와 action 동시 관리
4. 불변성 유지 용이: Immer.js를 사용하여 state를 직접 수정 가능하게 만듦
useState
를 활용useState
로 충분히 관리 가능한 상태는 Redux를 사용하지 않아도 됩니다.npm install @reduxjs/toolkit react-redux
store.js
import { configureStore } from "@reduxjs/toolkit"
export default configureStore({
reducer : { }
})
Redux 상태를 앱 전체에서 사용할 수 있도록 설정합니다.
index.js
import { Provider } from "react-redux";
import store from '../redux/store.js'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App /> //store.js에 있던 state 전부 사용가능
</Provider>
);
store.js
파일에서 상태와 리듀서를 정의하고 Redux Store를 구성합니다.
import { configureStore, createSlice } from "@reduxjs/toolkit";
// Slice 생성
let user = createSlice({
name: "user",
initialState: "kim", // 기본 상태 값
});
// Store 생성
export default configureStore({
reducer: {
user: user.reducer, // user 등록
},
});
createSlice
상태(state
)와 리듀서(reducer
)를 동시에 생성하는 함수.
Redux Toolkit에서 상태 관리를 간편하게 설정할 수 있도록 도와줍니다.
initialState
상태의 초기값을 지정합니다.
Slice가 처음 생성될 때 사용할 기본 상태 값입니다.
reducer
상태를 변경하는 함수입니다.
각 리듀서는 특정 액션에 따라 상태를 어떻게 업데이트할지 정의합니다.
Reducer에서 반환된 새로운 state를 Store라는 개체로 정리해 관리하는 곳이다.
let user = createSlice({
name: "user",
initialState: "kim",
reducers: {
changeName(state) {
return "john " + state;
},
},
});
export let { changeName } = user.actions; // 함수 export
import { useSelector, useDispatch } from "react-redux";
import { changeName } from "../store";
const Cart = () => {
const userName = useSelector((state) => state.user); // 상태 가져오기
const dispatch = useDispatch(); // 상태 변경 요청 함수
return (
<button
onClick={() => {
dispatch(changeName()); // 상태 변경 요청
}}
>
이름 변경
</button>
);
};
reducers
상태를 변경하는 함수들이다. 리듀서를 정의한다.
상태를 변경하는 함수. 어떤 액션이 발생했을 때 상태를 어떻게 변화시킬지 정의한다.
리듀서 함수
UpdateNumber(state, action){
return state = action.payload;
}
state
:state: 현재의 상태이다. initialState로 설정된 값이 처음 상태가 된다.action
: dispatch된 액션 객체이다. 액션 객체는 type과 payload를 가지고 있다. payload는 상태를 어떻게 변경할지에 대한 값을 포함한다.store.js
let user = createSlice({
name : 'user',
intialState : 'kim',
reducers : {
export changeName(state){
return 'john '+ state
}
}
})
위 코드와 같이 변경함수 앞에 export를 해주게 되면 작동하지 않는다. export는 createSlice()
함수 밖에서 사용해주어야 한다.
아래와 같이 작성해주어야 한다.
export let { changeName } = user.actions //함수명을 적어주면 된다. (destructuring)
createSlice()
함수 밖에서 작성한 코드 변경함수가 있는 createSlice이름.action
라고 적으면 된다.
Cart.jsx
import {useDispathch, useSelctor}
import { changename } from "../redux/store.js"
const Cart = ()=>{
let a = useSelector((state) => state); //redux store 가져오기
let dispatch = useDispatch() //dispatch는 store.js로 요청을 보내주는 함수이다.
return(
<>
<button onClick = {()=>{
dispatch(changeName()) //dispatch(state변경함수()) 이렇게 사용해주면 된다.
//그냥 state 변경함수()를 가져다 사용하지 말고 꼭 import 해주어야 한다.
}>
store.js한테 실행 부탁 메시지 보내는 버튼
</button>
</>
)
}
state가 array/object경우 return 없이 직접 수정할 수 있다.
❗️return
을 입력할 경우 에러가 발생한다.
→ 수정이 편리하기 때문에 문자 하나만 필요해도 {}
안에 담기도 한다.
store.js
let user = createSlice({
name : 'user',
initialState : {name : 'kim', age:20},
reducers : {
changeName(state){ //기존 state를 뜻한다.
state.name = "park"
},
increase(state, action){
state.age += action.payload //action.payload 화물보낸거 출력문법
},
}
})
action
이라고 많이 한다.action
에는 화물 뿐만 아니라 액션에 대한 여러가지 정보들이 들어 있기 때문이다.결론 : state 변경함수를 action이라고 한다.
store.js 안에 있는
let user
코드 길면 import export 사용하면 된다.
reducer
로 상태를 변경합니다.createAsyncThunk
와 extraReducers
를 사용합니다.reducer는 action creator를 자동으로 만들어준다.
액션(action) : Redux에서 상태를 변경하기 위해 사용하는 객체이다. 주로
type
과payload
를 가지고 있다.{ type: 'count/increment', payload: undefined }
액션 크리에이터(action creator): 액션 객체를 생성하는 함수입니다. 예를 들어:
const increment = () => ({ type: 'count/increment' });
결론 : type: 'count/increment'
자동으로 생성
name: 'count'
슬라이스 이름
reducers : { increment }
리듀서 함수 이름
extraReducers
는 외부에서 정의된 액션에 대한 리듀서를 추가할 때 리듀서를 추가할 때 사용된다.
즉, 현재 슬라이스 외부에서 정의된 액션 타입에 반응하도록 상태 변경 로직을 정의할 수 있다.
사용 목적
왜 액션 크리에이터를 안 만들어줄까?
reducers
현재 슬라이스에서 사용하는 동기적인 상태 변경 로직 작성
액션 크리에이터를 자동으로 생성합니다.
let user = createSlice({
name: "user",
initialState: "kim",
reducers: {
changeName(state) {
return "john " + state;
},
},
});
// 자동 생성된 액션 객체
{ type: "user/changeName", payload: undefined }
extraReducers
외부에서 정의된 액션에 대해 상태 변경 로직을 작성
액션 크리에이터는 자동 생성되지 않습니다.
extraReducers: (builder) => {
builder.addCase(fetchData.pending, (state) => {
state.status = "Loading";
});
builder.addCase(fetchData.fulfilled, (state, action) => {
state.value = action.payload;
state.status = "Complete";
});
builder.addCase(fetchData.rejected, (state) => {
state.status = "Failed";
});
};