[완성코드]https://codesandbox.io/s/react-redux-silseub-bfiys?file=/src/components/Todos.js
component/Counter.js
import React from "react";
const Counter = ({ number, onIncrease, onDecrease }) => {
return (
<div>
<h1>{number}</h1>
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
</div>
);
};
export default Counter;
component/TodoList.js
import React from "react";
const TodoItem = ({}) => {
return (
<div>
<input type="checkbox" />
<span>예제</span>
<button>삭제</button>
</div>
);
};
const Todos = ({}) => {
const onSubmit = (e) => {
e.preventDefault();
};
return (
<div>
<form onSubmit={onSubmit}>
<input />
<button type="submit">등록</button>
</form>
<div>
<TodoItem />
</div>
</div>
);
};
export default Todos;
App.js
import { Counter } from './component/Counter';
import React from 'react';
import { TodoList } from './component/TodoList';
export default function App() {
return (
<>
<Counter number={0} />
<hr />
<TodoList />
</>
);
}
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
// 액션 타입 정의
액션 타입 정의 : '모듈명/액션명'
export const increase = ()=>({type:INCREASE});
export const decrease = ()=>({type:DECREASE});
//액션 생성 함수 정의
export : 추후 이 함수를 다른 파일에서 불러와서 사용 가능하게 함.
const initialState = {
number: 0,
};
function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return {
number: state.number + 1,
};
case DECREASE:
return {
number: state.number - 1,
};
default:
return state;
}
}
export default counter;
export default : 단 한개만 내보낼 수 있음.
import counter from '..';
import {increase, decrease} from '..';
export const increase = ()=>({type:INCREASE});
export const decrease = ()=>({type:DECREASE});
export default counter;
module/todoList.js
const CHANGE_INPUT='todoList/CHANGE_INPUT';
const INSERT = 'todoList/INSERT';
const TOGGLE = 'todoList/TOGGLE';
const REMOVE = 'todoList/REMOVE';
export const changeInput= input => ({
type: CHANGE_INPUT,
input
});
let id = 3;
export const insert = text => ({
type:INSERT,
todo:{
id:id++,
text,
done:false
}
});
export const toggle = id => ({
type:TOGGLE,
id
});
export const remove = id => ({
type:REMOVE,
id
});
//액션 생성 함수 정의
const initialState = {
input: "",
todos: [
{ id: 1, text: "a", done: false },
{ id: 2, text: "b", done: false }
]
};
function todos(state = initialState, action) {
switch (action.type) {
case CHANGE_INPUT:
return {
...state,
input: action.input
};
case INSERT:
return {
...state,
todos: state.todos.concat(action.todo)
};
case TOGGLE:
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.id
? {
...todo,
done: !todo.done
}
: todo
)
};
case REMOVE:
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.id)
};
default:
return state;
}
}
export default todos;
- combineReducer : 기존에 만든 리듀서들을 하나로 만들어줌
module/index.js
import { combineReducers } from "redux";
import counter from "./counter";
import todos from "./todos";
const rootReducer = combineReducers({
counter,
todos
});
export default rootReducer;
import rootReducer from "./module";
src/index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import App from "./App";
import rootReducer from "./modules";
const store = createStore(rootReducer);
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
rootElement
);
- Provider 컴포넌트 : App이 Redux store에 접근 가능하게 해줌
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import App from "./App";
import rootReducer from "./modules";
const store = createStore(rootReducer);
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
yarn add redux-devtools-extension
src/index.js
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import App from "./App";
import rootReducer from "./modules";
const store = createStore(rootReducer, composeWithDevTools);
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
- 컨테이너 컴포넌트 : 리덕스 스토어와 연동된 컴포넌트
connect(mapStateToProps, mapDispatchToProps)(연동할 컴포넌트)
const makeContainer = connect(mapStateToProps, mapDispatchToProps); makeContainer(타깃 컴포넌트);
containers/CounterContainer.js
import { connect } from "react-redux";
import Counter from "../components/Counter";
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
const mapStateToProps = (state) => ({
number: state.counter.number
});
const mapDispatchToProps = (dispatch) => ({
increase: () => {
console.log("increase");
},
decrease: () => {
console.log("decrease");
}
});
export default connect(mapStateToProps, mapDispatchToProps)(CounterContainer);
import { connect } from "react-redux";
import Counter from "../components/Counter";
import { increase, decrease } from "../modules/counter";
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
const mapStateToProps = (state) => ({
number: state.counter.number
});
const mapDispatchToProps = (dispatch) => ({
increase: () => {
dispatch(increase());
},
decrease: () => {
dispatch(decrease());
}
});
export default connect(mapStateToProps, mapDispatchToProps)(CounterContainer);
import { connect } from "react-redux";
import Counter from "../components/Counter";
import { increase, decrease } from "../modules/counter";
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
export default connect(
(state) => ({
number: state.counter.number
}),
(dispatch) => ({
increase: () => {
dispatch(increase());
},
decrease: () => {
dispatch(decrease());
}
})
)(CounterContainer);
export default connect(
(state) => ({
number: state.counter.number,
}),
(dispatch) =>
bindActionCreators(
{
increase,
decrease,
},
dispatch,
),
)(CounterContainer);
export default connect(
(state) => ({
number: state.counter.number,
}),
{
increase,
decrease,
},
)(CounterContainer);
container/TodosContainer
import { connect } from "react-redux";
import Todos from "../components/Todos";
import { changeInput, insert, toggle, remove } from "../modules/todos";
const TodosContainer = ({
input,
todos,
changeInput,
insert,
toggle,
remove
}) => {
return (
<Todos
input={input}
todos={todos}
onChangeInput={changeInput}
onInsert={insert}
onToggle={toggle}
onRemove={remove}
/>
);
};
export default connect(
({ todos }) => ({
input: todos.input,
todos: todos.todos
}),
{
changeInput,
insert,
toggle,
remove
}
)(TodosContainer);
src/App.js
import CounterContainer from "./containers/CounterContainer";
import TodosContainer from "./containers/TodosContainer";
export default function App() {
return (
<div>
<CounterContainer />
<hr />
<TodosContainer />
</div>
);
}
component/Todos.js
import React from "react";
const TodoItem = ({ todo, onToggle, onRemove }) => {
return (
<div>
<input type="checkbox" onClick={() => onToggle(todo.id)} />
<span>{todo.text}</span>
<button onClick={() => onRemove(todo.id)}>삭제</button>
</div>
);
};
const Todos = ({
input, //인풋에 입력되는 값
todos, //할 일 목록이 있는 객체
onChangeInput,
onInsert,
onToggle,
onRemove
}) => {
const onSubmit = (e) => {
e.preventDefault();
onInsert(input);
onChangeInput("");
};
const onChange = (e) => onChangeInput(e.target.value);
return (
<div>
<form onSubmit={onSubmit}>
<input value={input} onChange={onChange} />
<button type="submit">등록</button>
</form>
<div>
{todos.map((todo) => (
<TodoItem todo={todo} onToggle={onToggle} onRemove={onRemove} />
))}
</div>
</div>
);
};
export default Todos;