6์ฃผ์ฐจ ๊ณผ์ : ๋ ธ๋ง๋์ฝ๋ <์ด๋ณด์๋ฅผ ์ํ ๋ฆฌ๋์ค 101> #3 React Redux & #4 Redux Toolkit ์๊ฐ
์ค์น: ํฐ๋ฏธ๋์
yarn add react-redux์ ๋ ฅ
์ค์น: ํฐ๋ฏธ๋์
yarn add react-router-dom์ ๋ ฅ
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from '../routes/Home';
import Detail from "../routes/Detail";
function App(){
return(
<Router>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/:id" element={<Detail/>}/>
</Routes>
</Router>
)
}
export default App;
Route๋ฅผ Routes๋ก ๊ฐ์ธ๊ณ ๊ทธ๊ฑธ ๋ Router๋ก ๊ฐ์ธ๋ ๋ฐฉ์
๊ธฐ๋ณธ์ ์ผ๋ก๋ ์์ ๊ฐ์ ์ฝ๋ ๊ตฌ์ฑ์ผ๋ก ๋์๊ฐ๋ค.
store๋ javascript์์ ํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์์ฑ!
๋ญ๊ฐ ๋ณํ๊ฐ ์ผ์ด๋๋ฉด ์ฐ๋ฆฌ application์ ๋ค์ renderํด์ฃผ๊ณ ์ถ๋ค.
๊ทธ๋ฐ๋ฐ ๋ฆฌ์กํธ๋ ๋ณํ๊ฐ ์ผ์ด๋๋ ๋ถ๋ถ๋ง ๋ ๋๋งํด์ค๋ค.
๊ทธ๋์ ์ฐ๋ฆฌ๋ react-redux๊ฐ ํ์ํ๋ค!
store์ ๋ณ๋์ฌํญ์ ๋ํด subscribeํ๊ณ ๊ฑ๋ค์ด ๋ฐ๋ ๋ ๋ชจ๋ ๊ฒ ๋ค์ ๋ ๋๋ง ๋๊ฒ ํด์ฃผ์.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from "./components/App";
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
index.js์ ๊ฐ์ ์ด๋ ๊ฒํด์ฃผ๋ฉด Provider๋ฅผ ํตํด ์ฑ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ Redux store๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
connect: components๋ค์ store์ ์ฐ๊ฒฐ์์ผ์ค๋ค.
๋ ์ข ๋ฅ์ argument์ ํจ๊ป ํธ์ถ๋๋ function
โท ์ฒซ ๋ฒ์งธ argument : Redux store์์ ์จ state
โท ๋ ๋ฒ์งธ argument : component์ props
...
function mapStateToProps(state) {
return { toDos: state };
}
export default connect(mapStateToProps)(Home);
...
์๋ฐ์์ผ๋ก ํด์ฃผ๋ฉด ์ด์ ์ฐ๋ฆฌ๋ todo๋ฅผ renderํ ์ ์๊ฒ ๋์๋ค~!

connect ํจ์์ ๋๋ฒ์งธ ์ธ์.
action์ reducer ํจ์์๊ฒ ๋ณด๋ด๋ ์ญํ ์ ๊ฐ์ง dispatch๋ฅผ props๋ก ๋ณด๋ผ ์ ์๋ค.
๐ฉ mapDispatchToProp์ ์ธ์
โท ์ฒซ ๋ฒ์งธ ์ธ์dispatch: Redux์store.dispatch()์ ๊ฐ์
โท ๋ ๋ฒ์งธ ์ธ์ownProps: ์๋ต๊ฐ๋ฅ. ์ปดํฌ๋ํธ๊ฐ ํ์ฌ ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ props๋ฅผ ๋ณด์ฌ์ค
connect๋ ์ฌ์ ํ ์ต์ ๋ฒ์ ์์๋ ์๋ํ์ง๋ง ๊ธฐ๋ณธ์ ์ผ๋ก hooks useSelector๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข๋ค๊ณ !
๊ณต์ ๋ฌธ์์์๋ ์ด์ connect๋ mapDispatchToProps๋ ์ฌ์ฉํ์ง ์๋๋ค๊ณ ํจ,,
์์ ์ฝ๋๋ฅผ Selector๋ฅผ ์ด์ฉํ์ฌ ๋ฐ๊ฟ๋ณด์
useSelector:
selectorํจ์๋ฅผ ์ด์ฉํ์ฌ redux store state์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๊ฒ ํด์ค
useSelector๋ฅผ ์ด์ฉํ๋ฉด store์ state๋ฅผ ๋ฐ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ค.
ex) const todo = useSelector((state)=>state);
useDispatch: Redux store์์
dispatchํจ์์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ฐํ (mapDispatchToProps๋์ ์ฌ์ฉ ๊ฐ๋ฅ)
ex)
const dispatch = useDispatch();
dispatch(addTodo(text));
import React, { useState } from "react";
import { useSelector,useDispatch } from "react-redux";
import { addToDo } from './../store';
function Home(){
const [text, setText] = useState("");
const toDo = useSelector((state)=>state);
const dispatch = useDispatch();
function onChange(e){
setText(e.target.value);
}
function onSubmit(e){
e.preventDefault();
console.log(text);
dispatch(addToDo(text));
setText("");
}
return(
<>
<h1>To Do</h1>
<form onSubmit={onSubmit}>
<input type="text" value={text} onChange={onChange}/>
<button>Add</button>
</form>
<ul>{JSON.stringify(toDo)}</ul>
</>
);
}
export default Home;
import React from "react";
import { useDispatch } from "react-redux";
import { deleteToDo } from "../store";
function ToDo({ text, id }) {
const dispatch = useDispatch();
const onBtnDelete = () => {
dispatch(deleteToDo(id));
};
return (
<li>
{text}<button onClick={onBtnDelete}>DEL</button>
</li>
);
}
export default ToDo;
import React, { useState } from "react";
import { useSelector,useDispatch } from "react-redux";
import { addToDo } from './../store';
import ToDo from "../components/ToDo";
function Home(){
const [text, setText] = useState("");
const toDos = useSelector((state)=>state);
const dispatch = useDispatch();
function onChange(e){
setText(e.target.value);
}
function onSubmit(e){
e.preventDefault();
console.log(text);
dispatch(addToDo(text));
setText("");
}
return(
<>
<h1>To Do</h1>
<form onSubmit={onSubmit}>
<input type="text" value={text} onChange={onChange}/>
<button>Add</button>
</form>
<ul>
{toDos.map(toDo => (
<ToDo {...toDo} key={toDo.id} />
))}
</ul>
</>
);
}
export default Home;
toDos๋ฅผ mapํจ์๋ฅผ ์ด์ฉํ์ฌ ์ค์ ํด์ฃผ๊ณ onClick์์ dispatchํจ์์ deleteToDo(id)๋ฅผ ๋ฃ์ด์ ์ญ์ ํด์ฃผ์. ๊ทธ onClick event๋ฅผ button์ ๋ฌ์์ฃผ๋ฉด ๋.
(๊ทผ๋ฐ ๊ฐ์ธ์ ์ผ๋ก ๊ฐ์๊ฐ ๊ณ~์ connect, mapDispatchToProps๋ฅผ ์ฌ์ฉํด์ selector๋ฅผ ์ด์ฉํ์ฌ ๋ฐ๊ฟ์ฃผ๋ ๊ทธ ๊ณผ์ ์ด ๋๋ฌด๋๋ฌด๋๋ฌด๋๋ฌด ๋ถํธํ๋ค,,, ๋ฆฌ๋ด์ผ ์๊ธ,,,,,,)
๊ฐ ํฌ๋์ ๋งํฌ๋ฅผ ๊ฑธ์ด์ detail ํ์ด์ง๋ก ๋์ด๊ฐ๊ฒ ํด์ค ๊ฒ์ด๋ค.
<li>
<Link to={`/${id}`}>
{text}
</Link>
<button onClick={onBtnDelete}>DEL</button>
</li>
์ด๋ ๊ฒ ํด์ฃผ๋ฉด ๊ฐ id์ ๋ฐ๋ผ detail ํ์ด์ง๋ก ๋์ด๊ฐ๊ฒ ์ง,,
์ด์ params๋ฅผ ๋ฐ์์์ผํ๋ค.

id์ Params๋ฅผ ๋ฐ์์จ ๋ค ์ฝ์์ ์ฐ์ด๋ณด๋ ์ ์ถ๋ ฅ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
import React from "react";
import { connect } from "react-redux";
import { useParams } from "react-router-dom";
function Detail({ toDos }) {
const myId = useParams().id;
const toDo = toDos.find((toDo) => toDo.id === parseInt(myId));
return (
<>
{toDo?.text} - Created at: {toDo?.id}
</>
);
}
function mapStateToProps(state) {
return { toDos: state };
}
export default connect(mapStateToProps)(Detail);
todo์ text๋ฅผ ๋ฐ์์์ ์ถ๋ ฅํ๊ณ ๊ทธ ์์ todo์ id๊น์ง ๊ฐ์ด ์ถ๋ ฅํ๋ ์ฝ๋์ด๋ค!
find๋ testing function์ ๋ง์กฑํ๋ ์ฒซ๋ฒ์งธ ์์๋ฅผ ๋ฐํํ๋ค.
ํด์ ์ฐ๋ฆฌ๋ toDo๋ฅผ findํด์ค ๊ฒ์ด๊ณ ์ด๋ parameter์ id์ ๊ฐ์ ๊ฒ์ ์ฐพ๋๋ค.
