리액트 상태 관리 라이브러리 리덕스 사용해보기
컴포넌트간 state를 공유할 목적으로 리덕스를 사용해볼 수 있다.
Context API로만으로도 충분한 프로젝트라면 리덕스를 사용할 필요는 없지만 프로젝트 규모가 크다면 리덕스 사용을 고려해보는 것이 좋다.
또, 비동기 작업을 자주 해야한다면 Context API보다 리덕스를 사용하는 것이 더 좋다.
Redux Toolkit 설치하기
npm i @reduxjs/toolkit react-redux
store.js 파일을 만든 후 다음과 같은 코드를 붙여넣으면 된다.
/* store.js */
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {},
})
스토어 파일을 만들었다면, index.js
파일로 가서 <Provider>
컴포넌트로 App 컴포넌트를 감싸주면 된다.
/* index.js */
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
react-redux의 Provider와 조금 전에 만든 store 파일을 import 해준 후
App 컴포넌트를 Provider
컴포넌트로 감싸주고 props로 store
파일을 넣어주면 된다.
먼저, store.js
파일에서 createSlice를 import 해왔다.
slice 파일을 별도로 만들어서 사용해도 될듯 하다.
import { configureStore, createSlice } from "@reduxjs/toolkit";
createSlice 사용방법
import { configureStore, createSlice } from '@reduxjs/toolkit';
const initialState = {
name: '아이유', // state 초기값
};
const personSlice = createSlice({
name: 'person', // slice 식별 이름 작명
initialState, // 초기값
});
export default configureStore({
reducer: {
person: personSlice.reducer,
},
});
createSlice에 slice 식별을 위한 문자열 이름을 person이라고 작명해주었고
초기 상태 값은 initialState 변수의 값으로 설정해주었다.
상태를 업데이트하는 리듀서 함수도 추가해주어야 하지만 일단 store에 작성한 슬라이스를 불러와보기로 했다.
export했기 때문에 사용하고자 하는 컴포넌트에 가서 import만 해주면 된다.
사용할 컴포넌트에서 다음과 같은 세팅이 필요하다.
import { useSelector } from 'react-redux';
useSelector
를 import 해오면 store에 저장한 상태를 가져올 수 있게 된다.
컴포넌트 안에서 store에 저장했던 데이터를 잘 불러와지는지 확인해보았다.
useSelector
를 사용할 때는 함수 형태로 사용해야 한다.
const test = useSelector((state) => {
return state;
});
console.log(test);
person state가 제대로 가져와지는 것을 확인할 수 있었다.
이처럼 props를 사용하지 않아도 redux를 사용해서 상태값을 불러올 수 있다.
조금더 사용법을 익혀보기 위해 slice를 하나 더 만들어보았다.
import { configureStore, createSlice } from '@reduxjs/toolkit';
const initialState = {
name: '아이유',
};
const personSlice = createSlice({
name: 'person',
initialState,
});
const testSlice = createSlice({
name: 'test',
initialState: ['💙', '💜', '💛'],
});
export default configureStore({
reducer: {
person: personSlice.reducer,
test: testSlice.reducer,
},
});
이번에는 initialState를 바깥이 아닌 안에서 작성해주었다.
마찬가지로 콘솔에 출력해보니 다음과 같이 나타나는 것을 확인할 수 있었다.
personSlice와 testSlice가 함께 출력되고 있는데 store에서 두가지를 함께 묶어서 보냈기 때문이다.
만약 여기서 하나의 slice 상태 값만 가져오고 싶다면,
const test = useSelector((state) => {
return state.person;
});
console.log(test);
위 코드처럼 useSelector를 사용해서state.person
과 같은 형태로 return 시켜주면
해당 state만 사용하도록 만들 수도 있다.
모든 state를 redux store에 등록할 필요는 없다.
공통적으로 사용되는 state를 store에 등록해서 사용하도록 하자.
state를 업데이트 시켜줄 함수를 만들어보자.
import { configureStore, createSlice } from '@reduxjs/toolkit';
const heart = createSlice({
name: 'heart',
initialState: ['❤', '🧡', '💛'],
reducers: {
addHeartColor(prevState) {
return [...prevState, '💚', '💙', '💜'];
// 참고 ) 리덕스는 Immer 라이브러리 있어서
// `...` 안써도 됨..!
},
changeHeartColor() {
return ['💙', '💜', '🤍'];
},
},
});
export const { addHeartColor, changeHeartColor } = heart.actions;
export default configureStore({
reducer: {
heart: heart.reducer,
},
});
불변성 유지를 위해 ...prevState를 작성했는데 찾아보니 리덕스는
immer
라이브러리 덕분에 오브젝트나 배열과 같은 state도 그냥 바로 변경시켜줘도 되는 것을 알았다.
스토어에 간단하게 slice를 만들어주고 createSlice 안에 reducers
를 추가해준 후
reducers에서 state 업데이트 함수를 정의해주면 된다.
그리고 export도 해줘야 사용할 수 있기 때문에 디스트럭처링 문법을 사용해서 export 해주었다.
reducers에 작성한 함수는 변수명.actions
로 접근 가능하다.
state 변경 시키기
import { useSelector, useDispatch } from 'react-redux';
import { addHeartColor, changeHeartColor } from '../store';
function Test() {
const heart = useSelector((state) => state.heart);
const dispatch = useDispatch();
const onClick = () => {
dispatch(addHeartColor());
};
const onClick2 = () => {
dispatch(changeHeartColor());
};
return (
<div style={{ width: '500px', height: '200px', padding: '50px' }}>
{heart.map((item, idx) => {
return <span key={`키 값은 귀찮으니 ${idx}`}>{item}</span>;
})}
<div>
<button onClick={onClick}>테스트1</button>
<button onClick={onClick2}>테스트2</button>
</div>
</div>
);
}
export default Test;
Test 컴포넌트를 하나 만들어서 App.js에서 렌더링 되도록 했다.
일단 useSelector는 앞서 살펴봤으니 생략하고, state 변경 함수를 사용하기 위해선 useDispatch
를 import 해주어야 한다. 아까 만든 함수도 함께 import 해주었다.
addHeartColor 함수의 경우 이전 state 값을 사용하도록 작성한 함수고
changeHeartColor 함수는 단순히 state값을 변경시켜주는 함수이다.
즉, 테스트1 버튼을 누르면 기존 하트에 💚, 💙, 💜 가 추가될 것이고,
테스트2 버튼을 누르면 💙, 💜, 🤍 만 렌더링 될 것이다.
버튼에 onClick 이벤트를 달아주고 해당 이벤트 함수에서 dispatch 함수를 호출하면서 만들어 주었던 state 변경 함수를 내부에서 호출시켜주면 된다.
테스트1 버튼 클릭
테스트2 버튼 클릭
아직은 익숙하지 않아서 여러차례 더 해봐야겠지만 리덕스 사용하기 전에 useReducer를 사용해본적이 있어서 그런지 action이라던가, initialState, dispatch 같이 낯익은 이름들이 보여서 조금 거부감이 덜한 것 같다. ㅋㅋㅋ
참고 자료
리덕스 툴킷 공식문서