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์ ๊ฐ์ ๊ฒ์ ์ฐพ๋๋ค.