Redux 를 예전에 복습했던 게시글이 있다. 심지어 이것도 상당히 오래된...
정확하게! 개념을 알아가야 툴킷이고 뭐고 더 쉽다고들 하는 zustand, recoil 등등 할 수 있을 것 같아서 다시 처음부터 정리 시작.
이제 그냥 ~ 이렇게 쓰는 거라~ 다들 이렇게 쓰니까~ 가 아니고 제대로 좀 알고 써보자 ..
Store
:상태를 관리하는 곳, 즉 저장소 Reducer
:상태를 변화시키는 곳, 즉 변수를 저장하는 곳Actions
:상태를 변화시키는 함수, 액션을 취하는 곳npm install @reduxjs/toolkit react-redux
나 이거 설치 안 하고 아니 왜 나 안 될까..? 한 적 있음 ㅠ_ㅠ
이거 설치 안하면 오류 났다고 말도 안 해주니(호의가 계속 되면 둘리인 줄 안다..) 꼭 꼭 설치하고 진행하쟈..
이제 남들 다 한다는 counter 실습을 해보자
src/app/store.js
일단 store를 생성하고, 그 안에 { configureStore } API를 가져온다.
import { configureStore } from "@reduxjs/toolkit";
const store = configureStore({
reducer: {},
});
export default store;
이 과정에서 Redux
저장소가 생성되고,
저장소를 검사할 수 있도록 Redux DevTools
확장도 자동으로 구성된다. (이게 리덕스와 리덕스 툴킷의 차이!)
configureStore:기존 createStore
와 달리, 여러 개의 인자 대신에 이름이 지정된 하나의 object를 받고, reducer을 넘겨 준다.
index.js 파일에서 Store
, Provider
를 import 해오고 <App />
을 <Provider>
로 감싸 store
를 props로 전달해준다. (import해온 store를 Provider 컴포넌트의 속성값으로 설정)
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
여기서 중괄호 오류가 있었는데, 여기는 나중에 한 포스팅에 적어 놓으려구 함.
Slice를 만들어줘서 action, reducer을 동시에 써준다.
counterSlice.js 파일을 만들어서, { createSlice } API를 가져온다.
슬라이스를 생성하려면 슬라이스를 식별하기 위한 문자열 이름, 초기 상태 값, 상태 업데이트 방법을 정의하는 하나 이상의 리듀서 함수가 필요하다.
슬라이스가 생성되면 생성된 Redux 액션 생성자와 전체 슬라이스에 대한 리듀서 기능을 내보낼 수 있다.
src/features/counter/counterSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
value: 0,
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
initialState
는 state 초기값
reducers
는 setState라고 생각하면 편하다.
increment, decrement, incrementByAmount같은 함수를 action
이라고 한다.
state와 action 이 두개의 인자를 받아온다.
state
는 현재 state값이고,
action
은 reducer 함수를 거쳐 새로운 state를 리턴 한다.
여기서 reducer를 리턴함으로써
슬라이스에 제공된 초기 상태 값에 대한 액세스를 제공하고, 지연 상태 초기화가 제공되면 호출되고 새로운 값이 반환된다.
Redux Toolkit의 createSlice 및 createReducer API는 올바른 변경 불가능한 업데이트가 "변경되는" 업데이트 로직을 작성할 수 있게한다. (이것도 리덕스와의 차이)
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
위에서 생성한 counterSlice에서 리듀서 함수를 가져와서 스토어에 추가해야 한다.
리듀서 매개변수 내부에 필드를 정의함으로써 store에 이 슬라이스 리듀서 함수를 사용하여 해당 상태에 대한 모든 업데이트를 처리하도록 지시한다.
이제 React-Redux Hook를 사용하여 React 구성 요소가 Redux 저장소와 상호 작용하도록 할 수 있다.
useSelector
를 사용하여 저장소에서 데이터를 읽고
useDispatch
를 사용하여 작업을 전달할 수 있다.
내부에 <Counter>
구성 요소가 있는 Counter.js 파일을 만든 다음 해당 구성 요소를 App.js로 가져오고 <App>
내부에서 렌더링.
src/features/counter/Counter.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from "./counterSlice";
export function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div style={{ margin: "40px" }}>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
);
}
useDispatch
는 state변경 함수 사용 후 전달해주기 위해 import
useSelector
은 store.js에서 상태관리하기 위해 import 한 것이다.
그리고 counterSlice에서 만든 state변경 함수들을 사용하기 위해서 import 해주었다.
dispatch 변수를 만들어서 useDispatch를 담아주고
dispatch(state변경함수()) 해서 사용해주면 된다.
counterSlice에서 만든
increment(state, action) -> action자리에
dispatch(increment(action자리))를 받아서 useDispatch를 통해 전달해준다.
그리고 꼭 action인자를 받아올때 action.payload를 붙여 써주어야 한다.
이제 "증가" 및 "감소" 버튼을 클릭할 때마다
<Counter>
구성 요소는 저장소의 새 상태 값을 보고 새 데이터로 자체를 다시 렌더링한다. console.log("count");
console.log(count);
store에 reducer를 여러 개 넣고 관리하는 것은 어떻게 하는지 궁금해서
또 따로 진행하게 되었다 ㅎ_ㅎ
features 폴더에 ChangeColor.js 파일을 만든다.
src/features/color/ChangeColor.js
import React, { useState } from "react";
function ChangeColor() {
const [color, setColor] = useState("");
return (
<div>
<input
type="text"
onChange={(e) => {
setColor(e.target.value);
}}
/>
<button>CHANGE COLOR</button>
</div>
);
}
export default ChangeColor;
theme.js라는 파일을 만든다.
src/features/color/theme.js
import { createSlice } from '@reduxjs/toolkit'
// initialState는 리듀서를 쓰다보면 자주 건들게 되는데, 매번 써주다 보면 귀찮아서 따로 빼주는 작업
// 여기는 어차피 하나밖에 없지만, 객체가 많을 때는 유용하다.
// string을 넘길 것이기 때문에 ""
const initialStateValue = "";
export const themeSlice = createSlice({
name: "theme",
initialState: { value: initialStateValue },
reducers: {
changeColor: ( state, action ) => {
state.value = action.payload
},
},
});
export const { changeColor } = themeSlice.actions;
export default themeSlice.reducer;
src/app/store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from "../features/counter/counterSlice";
import themeReducer from "../features/color/theme";
export default configureStore({
reducer:{
counter: counterReducer,,
theme: themeReducer
}
})
숫자의 색이 바뀌게 하고 싶으니 div에 스타일을 하나 줘서 인풋의 값이 css 요소로 들어가게 해야 한다.
일단 ChangeColor 에서 버튼 기능 + dispatch (나 수정했따!) 추가.
src/features/color/ChangeColor.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { changeColor } from "./theme";
function ChangeColor() {
const [color, setColor] = useState("");
const dispatch = useDispatch();
console.log(color);
return (
<div>
<input
type="text"
onChange={(e) => {
setColor(e.target.value);
}}
/>
<button
onClick={() => {
dispatch(changeColor(color));
}}
>
CHANGE COLOR
</button>
</div>
);
}
export default ChangeColor;
이건 이제 App.js에 추가해주고,
App.js
import React from "react";
import ChangeColor from "./features/color/ChangeColor";
import { Counter } from "./features/counter/Counter";
function App() {
return (
<div>
<Counter />
<ChangeColor />
</div>
);
}
export default App;
Counter 에 있는 애들 색상을 바꿀 거니깐,
아까 Counter에 추가해준다.
src/features/counter/Counter.js
...
const themeColor = useSelector((state) => state.theme.value);
...
return (
<div>
<div style={{ margin: "40px", color: themeColor }}>
버튼을 눌러보자.
...
</div>
input 창에 css에서 사용하는 색상명을 아무거나 적으면 그걸로 바뀐다.