기존 리액트에서는 state에 있는 것들을 자식 컴포넌트를 타고 하나한 내려보내야 했는데,
이것이 불편하기 때문에 리덕스라는 것이 등장했다.
리덕스는 설치해서 사용해야 한다.
npm install react-redux 로 설치한다.
Store
Action
Reducer
Dispatch
subscribe()
기존 리덕스를 좀 더 사용하기 쉽게 개선한 것이 리덕스 툴킷이다.
리덕스 툴킷도 설치가 필요하다.
npm install @reduxjs/toolkit 로 설치한다.
기존 리덕스에서 store는 단순히 전역 저장소 역할만 했으나, 툴킷에서는 리듀서를 등록하고
관리하는 역할도 한다.
// createStore() 는 deprecated 되었다.
// 그래서 configureStore 를 권장한다.
// store : state 를 관리하는 객체
// reducer : state 를 변화시키는 함수(이때 action 을 활용)
// dispatch : 특정 reducer 를 호출하는 함수
// subscribe : state 변화 후 호출되는 callback 함수
// redux toolkit 에서는 위 내용을 조금 더 통합적으로 관리.
// store 는 reducer 를 등록하고 관리함.
const Store = configureStore({
reducer: {
todo: TodoReducer
}
});
export default Store;
configureStore 를 사용해서 생성한다.
import { createSlice } from "@reduxjs/toolkit";
// reducer : slice 라고도 불리며, state 를 수정하는 함수와 해당 state 를 관리.
const TodoReducer = createSlice({
name: 'todo', // store 에 등록시 사용할 이름
initialState: { // state 초기값(여러 컴포넌트에서 사용할 수 있다)
idx: 3,
list: [
{ idx: 1, todo: '스프링 부트 공부', done: true },
{ idx: 2, todo: '리액트 공부', done: false },
{ idx: 3, todo: '리덕스 공부', done: false },
]
},
reducers: { // 위 state 를 관리할 리듀서들...
insert(state, action) { // state, 보내온 값(payload)
state.idx++;
let todo = { idx: state.idx, todo: action.payload, done: false };
state.list.push(todo);
return state; // reducer 는 항상 state 값 반환 필요.
},
toggle(state, action) {
let index = state.list.findIndex((item) => {
return item.idx === action.payload;
});
state.list[index].done = !state.list[index].done;
return state;
},
del(state, action) {
let index = state.list.findIndex((item) => {
return item.idx === action.payload;
});
state.list.splice(index, 1);
return state;
}
}
});
// slice 의 reducer 들을 외부에서 사용할 수 있도록 공개
export default TodoReducer.reducer;
그리고 리듀서는 createSlice로 생성하여 store 등록시 사용할 이름과 state 초기값을 지정하고, state 를 관리할 reducers 라는 곳에 사용할 함수들을 만든다.
참고로 리듀서는 슬라이스 라고도 불린다.
// npm install @reduxjs/toolkit 설치
// npm install react-redux
import { Provider } from "react-redux";
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import Store from "./redux/store.jsx";
// store 를 반드시 Provider 로 등록해야 한다.
createRoot(document.getElementById('root')).render(
<Provider store={Store}>
<App />
</Provider>
)
main.jsx 에서는 store를 Provider 로 등록해줘야 한다.
import { useState } from "react";
import Store from "../redux/store";
export default function Input() {
const [input, setInput] = useState('');
const keyUp = (e) => {
if (e.keyCode === 13) {
console.log(e);
insert();
}
}
const insert = () => { // slice 에 있는 reducer 를 호출
// type : '호출할 리듀서', payload : Action
Store.dispatch({ type: 'todo/insert', payload: input });
setInput('');
}
return (
<div>
<h2>해야할 일</h2>
<hr />
<input type="text" value={input} onKeyUp={keyUp}
onChange={(e) => setInput(e.target.value)} />
<button onClick={insert}>추가</button>
</div>
);
}
입력을 받는 부분인 Input.jsx 이다. 여기서는 TodoReducer.jsx 에서 등록한 insert 함수를 호출해서 사용할 수 있다. 등록한 함수를 사용할 때는 Store.dispatch({}) 를 사용하고,
type 에는 reducer의 이름과 함수명을 넣고, payload 는 전달할 매개변수인데, 우리가 입력하는 input 을 사용할 것이므로 input 을 넣는다.
import { useSelector } from "react-redux";
import Store from "../redux/store";
export default function List() {
let list = <div>등록된 아이템이 없습니다</div>;
// slice 의 state 값 가져오기
// useSelector 를 이용하면 모든 slice 의 state 값을 볼 수 있다.
let slices = useSelector((state) => {
console.log(state);
return state.todo.list;
});
list = slices.map((item) => {
return (<Item key={item.idx} item={item} />);
});
return (
<>
{list}
</>
);
}
// 어차피 list 에서만 쓸 것이므로 한 파일에 item 도 같이 넣음.
function Item({ item }) {
let class_name = item.done ? 'text done' : 'text';
const toggle = () => {
Store.dispatch({ type: 'todo/toggle', payload: item.idx });
}
const del = () => {
Store.dispatch({ type: 'todo/del', payload: item.idx });
}
return (
<div className="item">
<input type="checkbox" checked={item.done}
onChange={toggle} />
<div className={class_name}>{item.todo}</div>
<div className="delete" onClick={del}>삭제</div>
</div>
);
}
List.jsx 에서는 리스트들을 렌더링 한다.
우선 TodoReducer.jsx 에 있는 state 값을 가져오기 위해 useSelector 를 사용한다.
가져온 것을 하나하나 꺼내기 위해 map을 사용한다.
그것을 리턴문에 집어넣어 렌더링한다.
그리고 Item 함수에서는 toggle 과 del 함수를 TodoReducer.jsx 로부터 호출해서 사용한다.