이번에는 react-redux 라이브러리를 사용해서 react에서 어떻게 redux로 컴포넌트 관리를 하는지 알아 보겠습니다.
Component 구조/용어
- Provider 컴포넌트에 store 설정
-
connect 첫번째, 두번째 인자에 mapStateToProps, mapDispatchToProps function 설정
- 설정된 두 인자는 connect를 설정한 컴포넌트에 props로 사용된다.
-
mapStateToProps
- 컴포넌트 props로 state를 전달받아 넘기는 기능을 합니다.
-
mapDispatchToProps
- 컴로넌트 props로 dispatch기능을 넘기는 기능을 합니다.
index.js
: <Provider store={store}></Provider>
ㄴApp.js
: <Router>, <Route>
ㄴHome.js
: connect(mapStateToProps, mapDispatchToProps)(Home);
ㄴToDo.js
: connect(null, mapDispatchToProps)(ToDo);
ㄴDetail.js
: connect(mapStateToProps)(Detail);
Provivder 설정
-
Provider(react-redux)에 생성한 "store"를 설정함으로 store가 변경될때 App에서 store를 사용할 수 있다.
- Provider 설정 위치: store를 사요알 component에 컴포넌트 형식으로 감싸면 된다.
- [Study list]태그를 감싸는 형태로 어떻게 하위 컴포넌트에 store를 사용하는건지 확인해보자!
import React from "react" import ReactDOM from "react-dom" import App from "./component/App" import { Provider } from "react-redux" import store from "./store" ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") )
2. Router에 생성한 컴포넌트 Route에 설정
- app.js
import React from "react"
import { HashRouter as Router, Route } from "react-router-dom"
import Home from "../routes/Home"
import Detail from "../routes/Detail"
function App() {
return (
<Router>
<Route path="/" exact component={Home} />
<Route path="/:id" exact component={Detail} />
</Router>
)
}
export default App
3. connect에 대해서
-
react-redux 라이브러리 함수 입니다.
- import { connect } from "react-redux";
-
사용 모습을 보면 '커링'기법이 사용됨을 알수 있다.
- connect(mapStateToProps, mapDispatchToProps)(Home);
- 커링 예제
var curryFuncTest = function(func) { return function(a) { return function(b) { // getMaxWith10, getMinWith10 funciton return func(a, b) } } } var getMaxWith10 = curryFuncTest(Math.max)(10) console.log(getMaxWith10(8)) console.log(getMaxWith10(25)) var getMinWith10 = curryFuncTest(Math.min)(10) console.log(getMinWith10(8)) console.log(getMinWith10(25))
3.1 connect fucntion의 첫번째 arguments
- 공식문서
- component와 store를 연결해주는 react-redux function
-
아래와 같이 써주게 되면 Home component에 mapStateToProps 함수에서 반환한 객체를 사용 할 수 있다.
- connect(mapStateToProps)(Home);
- store.getState()라고 보면 되겠다.
3.2 connect function의 두번째 arguments
- 공식문서
- store.dispatch()를 구현하기 위한 것
- 아래와 같이 mapDispatchToProps function을 작성해 connect 두번째 파라미터로 넘기면 component에 dispatch를 사용해 store에 있는 action을 사용해서 store state를 변경합니다.
-
mapDispatchToProps에서 반환한 function을 수행하면 dispatch가 수행되어 state가 변경 됩니다.
- connect(mapStateToProps, mapDispatchToProps)(Home);
import React, { useState } from "react"
import { connect } from "react-redux"
import { actionCreators } from "../store"
import ToDo from "../component/ToDo"
function Home({ toDos, addToDo, dispatch }) {
console.log("### Home > component: ", { toDos, addToDo, dispatch })
const [text, setText] = useState("")
function onChange(e) {
setText(e.target.value)
}
function onSubmit(e) {
console.log("### Home > onSubmit: ", text)
e.preventDefault()
setText("")
// #dispatch 하는 방법
// dispatch(actionCreators.addTodo(text));
addToDo(text)
}
return (
<>
<h1>To Do</h1>
<form onSubmit={onSubmit}>
<input type="input" value={text} onChange={onChange}></input>
<button>Add</button>
</form>
<ul>
{toDos.map(toDo => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
</>
)
}
//connect를 사용해서 Home으로 보내주는 props에 추가 될 수 있도록 허용
//mapStateToProps return에 보내주는 값은 Home props로 받을 수 있다.
function mapStateToProps(state, ownProps) {
console.log("### Home > mapStateToProps: ", { state, ownProps })
return { toDos: state }
}
function mapDispatchToProps(dispatch, ownProps) {
console.log("### Home > mapDispatchToProps: ", { dispatch, ownProps })
return {
dispatch,
addToDo: text => dispatch(actionCreators.addToDo(text)), //disaptch에 action을 넣어준 것이다.
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home)
3.3 connect 정리
- component에서 comnnect를 사용해서 store.js에 있는 store에 대해서 dispatch, action Creators를 처리 할 필요가 없다.
- connect에 두개의 function으로 state와 dispatch 객체들을 컴포넌트 props로 넘겨 사용할 수 있게 됐다.
4. delete 구현
-
ToDo component에 del btn이 있다!
- 이 버튼에 delete dispatch하는데 필요한 정보 3가지(아래 참고)를 활용해
- mapDispatchToProps를 만들어 button click event에서 사용하도록 한다.
-
dispatch하는데 필요한 정보 3가지
- store,
- actionCreator,
- redux dispatch
: (connect의 두번째 파라미터 function의 첫번째 파라미터 dispatch === mapDispatchToProps function의 첫번째 파라미터)을 사용해
import React from "react" import { connect } from "react-redux" import { actionCreators } from "../store" import { Link } from "react-router-dom" function ToDo({ text, id, onDeleteBtnClick }) { console.log("### ToDo > ToDo: ", { text, id, onDeleteBtnClick }) return ( <li key={id}> <Link to={`/${id}`}> {text} <button onClick={onDeleteBtnClick}>DEL</button> </Link> </li> ) } function mapDispatchToProps(dispatch, ownProps) { console.log("### ToDo > mapDispatchToProps: ", { dispatch, ownProps }) return { onDeleteBtnClick: () => dispatch(actionCreators.deleteToDo(parseInt(ownProps.id))), } } export default connect(null, mapDispatchToProps)(ToDo)
5. Detail Page
-
Home에서 todo list를 작성하고 Link를 통해서 Detail로 넘어길 때
- Detail component 화면로드 시 mapStateToProps에서 state는 현재 입력한 toDos 배열이다.
- mapStateToProps 첫번째, 두번째 파라미터 정보
- state = store.getState();
- ownProps - Link(react-router-dome)의 정보( { history, location, match, staticContext })
import React from "react" import { connect } from "react-redux" // import { useParams } from "react-router-dom"; function Detail({ toDo }) { // const id = useParams(); // console.log(id); console.log("### Detail > component: ", { toDo }) return ( <> <h1>{toDo?.text}</h1> <h5>Created at: {toDo?.id}</h5> </> ) } function mapStateToProps(state, ownProps) { console.log("### Detail > mapStateToProps: ", { state, ownProps }) const { match: { params: { id }, }, } = ownProps return { toDo: state.find(toDo => toDo.id === parseInt(id)) } } export default connect(mapStateToProps)(Detail)
react-redux, redux 비교
-
react-redux에서 사용하는 다음 두가지는(mapStateToProps, mapDispatchToProps) vanilla redux에서는 아래와 같은 역할을 합니다.
- mapStateToProps: store.getState()
- mapDispatchToProps: store.dispatch()