Learning Redux
// src/index.js
const add = document.getElementById("add");
const minus = document.getElementById("minus");
const number = document.querySelector("span");
let count = 0;
number.innerText = count;
const updateText = () => {
number.innerText = count;
};
const handleAdd = () => {
count = count + 1;
updateText();
};
const handleMinus = () => {
count = count - 1;
updateText();
};
add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);
<body>
<button id="add">Add</button>
<span></span>
<button id="minus">Minus</button>
</body>
이 코드를 리덕스로 바꿔보자
npm add redux
action 사용하는 방법.
store.dispatch()
메소드를 사용한다. 인자는 type
속성을 포함한 객체로 전달한다. (객체에 다른 정보를 넣어 reducer 안에 전달할 수 있다.)dispatch
는 해당 객체를 reducer
의 action
으로 전달하고 reducer
는 action
에 해당하는 modify
를 한다.store
를 만든다 :createStore
함수에 reducer
함수(countModifer)를 넣어준다store.subscribe()
메소드를 사용해 데이터를 추적한다.
store
란?
데이터를 저장하는 장소이다 ex)state
>reducer
란?
데이터를 modify 하는 함수이다.
reducer가 리턴하는 값이 새로운 데이터값이 된다.
reducer 는 현재의 state와 함께 불려진다
// redux 적용
import { createStore } from "redux";
const add = document.getElementById("add");
const minus = document.getElementById("minus");
const number = document.querySelector("span");
number.innerText = 0;
const ADD = "ADD";
const MINUS = "MINUS";
const countModifier = (count = 0, action) => {
switch (action.type) {
case ADD:
return count + 1;
case MINUS:
return count - 1;
default:
return count;
}
};
const countStore = createStore(countModifier);
const onChange = () => {
number.innerText = countStore.getState();
};
countStore.subscribe(onChange);
const handleAdd = () => {
countStore.dispatch({ type: ADD });
};
const handleMinus = () => {
countStore.dispatch({ type: MINUS });
};
add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);
<h1>To Dos</h1>
<form>
<input type="text" placeholder="Write to do" />
<button>Add</button>
</form>
<ul></ul>
import { createStore } from "redux";
const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");
const ADD_TODO = "ADD_TODO";
const DELETE_TODO = "DELETE_TODO";
const reducer = (state = [], action) => {
console.log(action);
switch (action.type) {
case ADD_TODO:
return [];
case DELETE_TODO:
return [];
default:
return state;
}
};
const store = createStore(reducer);
const onSubmit = (e) => {
e.preventDefault();
const toDo = input.value;
input.value = "";
store.dispatch({ type: ADD_TODO, text: toDo });
};
form.addEventListener("submit", onSubmit);
Single source of truth
state 는 read-only 이다.
reducer 는 순수함수로 작성한다.
react-redux 는 store를 구독하고, 변경사항에 대해 리랜더링 하게 해준다.
// 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
root.render(
<Provider store={store}>
<App />
)
</Provider>
컴포넌트
를 store
에 연결시켜준다.connect(getCurrentState)(Component)
function mapStateToProps(state, ownProps?)
state
: store 에서 오는 state
ownProps
: 컴포넌트에서 오는 props
{toDos}
mapStateToProps
함수의 리턴값은 Home
컴포넌트의 prop
에 추가된다.(connect 함수가 한 일 )function mapDispatchToProps(dispatch, ownProps?)
ownProps
는 컴포넌트가 받아내는 props 가 들어온다Home.js
// routes/Home.js
function Home({toDos}){
// logic
return (...)
}
function mapStateToProps(state) {// state 를 받아 props로 리턴하는 함수. store.getState() 역할
return { toDos: state };
}
function mapDispatchToProps(dispatch) {
return {
addToDo: (text) => dispatch(actionCreators.addToDo(text)),
};
}
// store에 있는 state를 Home으로 가져와 연결시킨다.
export default connect(mapStateToProps, mapDispatchToProps)(Home);
store.js
// 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;
ToDo.js
// ToDo.js
import React from "react";
import { connect } from "react-redux";
import { actionCreators } from "../store";
import { Link } from "react-router-dom";
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
// Detail.js
import React from "react";
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);