1. ToDo App의 기본 골격 만들기
index.html
<body>
<div id="root"></div>
</body>
/components/App.js
import React from "react";
function App() {
return "APP"
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(<App />, document.getElementById("root");
2. Router 설정
/component/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';
import '../scss/style.scss';
function App() {
return (
<Router>
<Route path="/" exact component={Home}></Route>
<Route path="/:id" exact component={Detail}></Route>
</Router>
);
}
export default App;
/routes/Home.js
export default () => "Home;
/routes/Detail.js
export default () => "Detail;
3. Home 페이지 구성하기
/routes/Home.js
import React, { useState } from "react"
function Home() {
const [ text, setText ] = useState("");
function onChange(e) {
setText(e.target.value);
}
function onSubmit(e) {
e.preventDefault();
setText("");
}
return (
<>
<h1>To Do</h1>
<form onsubmit={onSubmit}>
<input type="text" value={text} onChange={onChange}/>
<button>Add</button>
</form>
<ul></ul>
</>
);
}
export default Home;
1. React에 Redux 도입하기
store.js
import { createStore } from "redux";
const ADD = "ADD";
const DELETE = "DELETE";
const addToDo = (text) => {
return {
type: ADD,
text
};
};
const deleteToDo = (id) => {
return {
type: DELETE,
id: parseInt(id)
};
};
const reducer = ( state = [], action ) => {
switch (action.type) {
case ADD:
return [{ text: action.text, id: Date.now() }, ...state];
case DELETE:
return state.filter(toDo => toDo.id !== parseInt(action.id));
default:
return state;
}
}
const store = createStore(reducer);
export const actionCreators = {
addToDo,
deleteToDo
};
export default store;
index.js
react-redux의 <Provider>
import 하기
: The component makes the Redux store available to any nested components that need to access the Redux store.
→ 컴포넌트는 Redux store에 접근이 필요한 내장된 component들이 Redux store을 사용할 수 있게 해줍니다.
<Provider>
는 인자로 store를 받는다.
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById("root")
);
2. store의 state를 Home component로 가져오기
connect( mapStateToProps, mapDispatchToProps )
export default connect(mapStateToProps, mapDispatchToProps) (Home);
mapStateToProps( state, ownProps )
connect의 1번째 인자. component에서 store의 state를 가져와서 읽을 수 있게 해준다.
인자로 store의 현재 state인 state
와 기존 props인 ownProps
를 받는다.
→ state
와 ownProps
를 이용하여 필요한 정보를 가공 후 return하면 됨.
→ return type은 객체
읽어온 state를 component의 props로 넣어준다.
→ connect
함수가 component로 보내지는 props에 끼어드는 형식
/component/Home.js
import { connect } from 'react-redux';
import { actionCreators } from "../store";
function Home({ toDos }) {
//...
return(
...
<ul>
{ toDos.map(toDo => <li key={toDo.id}>{toDo.text}</li> )}
</ul>
)
}
function mapStateToProps(state) {
return { toDos: state }
}
export default connect(mapStateToProps) (Home);
3. Home component에서 store의 state를 변경하기 (dispatch)
mapDispatchToProps
dispatch
와 기존 props인 ownProps
(생략 가능)를 받는다.function mapStateToProps(state) {
return { toDos: state }
}
function mapDispatchToProps(dispatch) {
return {
addToDo: (text) => dispatch(actionCreators.addToDo(text))
}
}
export default connect(mapStateToProps, mapDispatchToProps) (Home);
이처럼 component에서는 state/dispatch만 하면 되고, store의 세부적인 구현(action creator, dispatch ~ action, subscription 등)은 store에서 처리하기 때문에 코드가 깔끔해진다.
4. ToDo component 만들기, 삭제 기능 구현
/components/ToDo.js
<li>...</li>
부분을 별도의 component로 구현import React from "react";
function ToDo({ text, onBtnClick }) {
return (
<li>
{text}
<button onClick={onBtnClick}>DEL</button>
</li>
);
}
function mapDispatchToProps(dispatch, ownProps){
return {
onBtnClick : () => dispatch(actionCreators.deleteToDo(ownProps.id))
};
}
export default connect(null, mapDispatchToProps) (ToDo);
5. Detail page 구현
/components/ToDo.js
...
import { Link } from "react-router-dom";
function ToDo({ text, onBtnClick, id }) {
return (
<li>
<Link to={`/${id}`} >
{text}
</Link>
<button onClick={onBtnClick}>DEL</button>
</li>
)
}
/routes/Detail.js
Detail page의 id 값을 가져오는 방법
useParams
hooks를 이용하여 params.id
로 값을 받아온다.
mapStateToProps
를 이용하여 ownProps.match.params.id
로 값을 받아온다.
→ Redux 연습을 위해 2의 방법 선택
import React from "react"
import { connect } from "react-redux";
function Detail({.toDo }) {
return (
<>
<h1>Detail</h1>
<h5>Created at: {toDo.id}</h5>
</>
)
}
function mapStateToProps(state, ownProps) {
const {
match: {
params: { id }
}
} = ownProps;
return { toDo: state.find(toDo => toDo.id == parseInt(id) ) };
}
새로 고침시 toDo list가 초기화 되면서 error가 발생. → toDo?.text
로 해결.
store.js
//...
const localStorageStore = JSON.parse(window.localStorage.getItem("toDoStore"))
const reducer = ( state = localStorageStore, action) => {
//...
}
const store = createStore(reducer);
const saveDataOnLocalStorage = () => {
const currentState = store.getState();
window.localStorage.setItem("toDoStore", JSON.stringify(currentState));
}
store.subscribe(saveDataOnLocalStorage);
//...