React, Redux, To-Do List
2024. 12. 30. 15:18ㆍ웹 개발
npm install redux
npm install @reduxjs/toolkit
Main.jsx
import './index.css';
import App from './App.jsx';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './modules';
// `createStore` 대신 `configureStore`, `configureStore`는 자동으로 Redux DevTools를 지원함
const store = configureStore({
reducer: rootReducer,
});
// `createRoot` 사용
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
App.jsx
import './App.css';
import CounterContainer from './containers/CounterContainer';
import TodosContainer from './containers/TodosContainer';
const App = () => {
return (
<div>
<CounterContainer />
<hr />
<TodosContainer />
</div>
);
};
export default App;
/containers/CounterContainer.jsx
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,
}),
{
increase,
decrease,
}
)(CounterContainer);
/containers/TodosContainer.jsx
import { connect } from 'react-redux';
import { changeInput, insert, toggle, remove } from '../modules/todos';
import Todos from '../components/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);
/modules/index.jsx
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers({
counter,
todos,
});
export default rootReducer;
/modules/counter.jsx
const INCREASE = 'counter/INCREASE'; // 액션 타입 정의
const DECREASE = 'counter/DECREASE';
export const increase = () => ({ type: INCREASE }); // 액셩 생성 함수 정의
export const decrease = () => ({ type: DECREASE });
const initialState = {
// 초기 상태 설정
number: 0,
};
const 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;
/modules/todos.jsx
const CHANGE_INPUT = 'todos/CHANGE_INPUT'; // 액션 타입 정의
const INSERT = 'todo/INSERT';
const TOGGLE = 'todos/TOGGLE';
const REMOVE = 'todos/REMOVE';
export const changeInput = (input) => ({
type: CHANGE_INPUT,
input,
});
let idx = 3;
export const insert = (text) => ({
type: INSERT,
todo: {
id: idx++,
text,
done: false,
},
});
export const toggle = (id) => ({
type: TOGGLE,
id,
});
export const remove = (id) => ({
type: REMOVE,
id,
});
const initialState = {
input: '',
todos: [
{
id: 1,
text: '리덕스 기초 배우기',
done: true,
},
{
id: 2,
text: '리액트와 리덕스 사용하기',
done: false,
},
],
};
const 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;
/components/Counter.jsx
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;
/components/Todos.jsx
const TodoItem = ({ todo, onToggle, onRemove }) => {
return (
<div>
<label style={{ cursor: 'pointer' }}>
<input
type="checkbox"
onClick={() => onToggle(todo.id)}
checked={todo.done}
readOnly={true}
/>
<span style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
{todo.text}
</span>
</label>
<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 type="text" value={input} onChange={onChange} />
<button type="submit">등록</button>
</form>
<div>
{todos.map((todo) => (
<TodoItem
todo={todo}
key={todo.id}
onToggle={onToggle}
onRemove={onRemove}
/>
))}
</div>
</div>
);
};
export default Todos;
https://stackblitz.com/edit/vitejs-vite-bzwnnpg5?embed=1&file=src%2Fmain.jsx
'웹 개발' 카테고리의 다른 글
React, Redux, Connetc 함수 대신 useSelect 사용하기 (0) | 2024.12.30 |
---|---|
React, Redux-actions 라이브러리 (1) | 2024.12.30 |
React + Redux, useStore, useDispatch (1) | 2024.12.27 |
React, Redux (1) | 2024.12.26 |
React, Context, useContext Hook (1) | 2024.12.26 |