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';
index.html 수정
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Vanilla Redux</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Index.js 수정 - 2
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(<App></App>, document.getElementById('root'));
react-redux와 react-router-dom을 설치하자
⇒ yarn add react-redux react-router-dom
routes 폴더를 생성해 Home.js와 Detail.js를 생성
// Home.js
export default () => 'Home';
// Detail.js
export default () => 'Detail';
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>
<Route path='/:id' exact component={Detail}></Route>
</Router>
);
}
export default App;
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();
console.log(text);
}
return (
<>
<h1>To Do</h1>
<form onSubmit={onSubmit}>
<input type='text' value={text} onChange={onChange} />
<button>Add</button>
</form>
<ul></ul>
</>
);
}
export default Home;
store.js 생성
import { createStore } from 'redux';
const ADD = 'ADD';
const DELETE = 'DELETE';
export const addToDo = (text) => {
return {
type: ADD,
text
};
};
export const deleteToDo = (id) => {
return {
type: DELETE,
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 !== action.id);
default:
return state;
}
};
const store = createStore(reducer);
export default store;
Index.js 수정
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import Provider from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
redux state로부터 정보를 가지고 와보자
💡 connect
: components들을 store에 연결시켜준다
Home.js 수정
import React, { useState } from 'react';
import { connect } from 'react-redux';
function Home({ toDos }) {
const [text, setText] = useState('');
function onChange(e) {
setText(e.target.value);
}
function onSubmit(e) {
e.preventDefault();
console.log(text);
}
return (
<>
<h1>To Do</h1>
<form onSubmit={onSubmit}>
<input type='text' value={text} onChange={onChange} />
<button>Add</button>
</form>
<ul>{JSON.stringify(toDos)}</ul>
</>
);
}
function mapStateToProps(state) {
return { toDos: state };
}
export default connect(mapStateToProps)(Home);
Home.js
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { actionCreators } from '../store';
function Home({ toDos, addToDo }) {
const [text, setText] = useState('');
function onChange(e) {
setText(e.target.value);
}
function onSubmit(e) {
e.preventDefault();
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(toDos)}</ul>
</>
);
}
function mapStateToProps(state) {
return { toDos: state };
}
function mapDispatchToProps(dispatch) {
return {
addToDo: (text) => {
dispatch(actionCreators.addToDo(text));
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
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
};
};
const reducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [{ text: action.text, id: Date.now() }, ...state];
case DELETE:
return state.filter((toDo) => toDo !== action.id);
default:
return state;
}
};
const store = createStore(reducer);
export const actionCreators = {
addToDo,
deleteToDo
};
export default store;
toDo.js 생성
import React from 'react';
import { connect } from 'react-redux';
import { actionCreators } from '../store';
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);
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 !== action.id);
default:
return state;
}
};
const store = createStore(reducer);
export const actionCreators = {
addToDo,
deleteToDo
};
export default store;
Home.js 수정
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { actionCreators } from '../store';
import ToDo from '../components/ToDo';
function Home({ toDos, addToDo }) {
const [text, setText] = useState('');
function onChange(e) {
setText(e.target.value);
}
function onSubmit(e) {
e.preventDefault();
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>
</>
);
}
function mapStateToProps(state) {
return { toDos: state };
}
function mapDispatchToProps(dispatch) {
return {
addToDo: (text) => {
dispatch(actionCreators.addToDo(text));
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
ToDo.js 수정
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { actionCreators } from '../store';
function ToDo({ text, onBtnClick, id }) {
return (
<li>
<Link to={`/${id}`}>
{text} <button onClick={onBtnClick}>DEL</button>
</Link>
</li>
);
}
function mapDispatchToProps(dispatch, ownProps) {
return {
onBtnClick: () => dispatch(actionCreators.deleteToDo(ownProps.id))
};
}
export default connect(null, mapDispatchToProps)(ToDo);
Detail.js 수정
import React from 'react';
import { useParams } from 'react-router-dom';
import { connect } from 'react-redux';
function Detail({ toDo }) {
return (
<>
<h1>{toDo?.text}</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)) };
}
export default connect(mapStateToProps)(Detail);
💪 Challenge