할 일 수정 기능 구현
1 - 자바스크립트 공부하기
2 - 리액트 공부하기
3 - 할일 목록 앱 만들기
App 컴포넌트에 todos 상태변수에 값을 수정(checked 값 토글)하는 함수를 정의하고, 해당 함수를 TodoList 컴포넌트의 props로 전달
루프를 돌리던지 x아니예요
특정객체의 특정값을 바꾸는거예요.
배열의 개수는 그대로 유지되면서 특정값만 바꿀떄, 토글을 쓰는거예요
코드 이해하기!!
App 컴포넌트에 todos 상태변수에 값을 수정(checked 값 토글)하는 함수를 정의하고, 해당 함수를 TodoList 컴포넌트의 props로 전달
import { useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function App() {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: "자바스크립트 공부하기" },
{ id: 2, checked: false, text: "리액트 공부하기" },
{ id: 3, checked: false, text: "할 일 목록 앱 만들기" },
]);
const nextId = useRef(4);
const insertTodo = text => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
const removeTodo = id => {
const newTodos = todos.filter(todo => todo.id !== id);
setTodos(newTodos);
};
const toggleTodo = id => {
/* 이건 루프로 돌린 코드얌.
const newTodos = [];
for (let i = 0; i < todos.length; i++) {
const todo = todos[i];
if (todo.id === id) {
newTodos.push({ ...todo, checked: !todo.checked });
} else {
newTodos.push(todo);
}
}
*/
const newTodos = todos.map(todo => todo.id === id ? { ...todo, checked: !todo.checked } : todo);
setTodos(newTodos);
};
return (
<TodoTemplate>
<TodoInsert insertTodo={insertTodo} />
<TodoList todos={todos} removeTodo={removeTodo} toggleTodo={toggleTodo} />
</TodoTemplate>
);
}
export default App;
const newTodos = todos.map(todo => todo.id === id ? { ...todo, checked: !todo.checked } : todo);
setTodos(newTodos);
소스코드 빌드
C:\react\todo-app>npm install http-server -g
글로벌하게 설치!!
C:\react\todo-app\build 디렉터리
2025-01-07 오전 10:42
.644 index.html 시작페이지처럼 동작한당
전체, 이파일들 호출, 빌드된 JS파일을 읽어들이는것,
npx http-server ! 웹서버를 실행하는것, 웹다큐먼트루트 디렉토리로 설정해준다, 현재디렉터리를 .
Available on:
http://192.168.45.192:8080
http://127.0.0.1:8080
Hit CTRL-C to stop the server
8080서버로 이동 : 로컬호스트는 웹서버 엔드포인트!!! 이서버뒤에는
웹 다큐먼트루트를 기본페이지를 읽어들이는것이야!!
브라우저로 실행 :
보여주는 이유는? 내가만든 어플리케이션 배포시!
웹서버가 있으면, 홈디렉토리에 npm build 한 결과물을 갖다놔야해요
<TodoTemplate>
<TodoInsert insertTodo={insertTodo} />
<TodoList todos={todos} removeTodo={removeTodo} toggleTodo={toggleTodo} />
</TodoTemplate>
컨텍스트 API와 유즈컨텍스트 훅을 이용해서 insertTodo, removeTdo, toggleTodo함수와 tods변수를 props 변수로 전달하지 않고 사용하게 수정해보자!!!
아래표는 스터디내용을 정리:
기능 | 구현된 위치 | 설명 |
---|---|---|
할 일 추가 | App , TodoInsert | App 컴포넌트에서 todos 상태변수에 새로운 항목을 추가하는 insertTodo 함수를 정의하고, 이를 TodoInsert 컴포넌트에 props로 전달. TodoInsert 에서 버튼 클릭 시 insertTodo 호출. |
할 일 삭제 | App , TodoList , TodoListItem | App 컴포넌트에서 todos 상태변수에서 항목을 삭제하는 removeTodo 함수를 정의하고, 이를 TodoList 컴포넌트에 전달. TodoList 에서 removeTodo 를 TodoListItem 으로 전달 후 삭제 버튼 클릭 시 호출. |
할 일 수정 | App , TodoList , TodoListItem | App 컴포넌트에서 todos 상태변수의 checked 값을 토글하는 toggleTodo 함수를 정의하고, 이를 TodoList 컴포넌트에 전달. TodoList 에서 toggleTodo 를 TodoListItem 으로 전달 후 체크박스 클릭 시 호출. |
문제풀기
1 Context API와 useContext 훅을 이용해서
2 insertTodo, removeTodo, togggleTodo 함수와
3 todos 변수를 props 변수로 전달하지 않고 사용할 수 있도록 수정 하는 코드
Context API
와 useContext
훅을 사용하면 props를 통해 반복적으로 데이터를 전달하지 않고도 컴포넌트 간 상태를 공유할 수 있습니다. 아래는 Context API
와 useContext
를 사용하여 insertTodo
, removeTodo
, toggleTodo
함수와 todos
변수를 관리하는 수정된 코드입니다.
TodoContext
를 생성하여 todos
상태와 함수들을 관리합니다.
import React, { createContext, useContext, useState, useRef } from "react";
// Context 생성
const TodoContext = createContext();
// Context Provider 컴포넌트
export const TodoProvider = ({ children }) => {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: "자바스크립트 공부하기" },
{ id: 2, checked: false, text: "리액트 공부하기" },
{ id: 3, checked: false, text: "할 일 목록 앱 만들기" },
]);
const nextId = useRef(4);
const insertTodo = (text) => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
const removeTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
const toggleTodo = (id) => {
const newTodos = todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
);
setTodos(newTodos);
};
return (
<TodoContext.Provider value={{ todos, insertTodo, removeTodo, toggleTodo }}>
{children}
</TodoContext.Provider>
);
};
// Context Consumer 훅
export const useTodos = () => useContext(TodoContext);
TodoProvider
로 앱 전체를 감쌉니다.
import React from "react";
import { TodoProvider } from "./TodoContext";
import TodoTemplate from "./components/TodoTemplate";
import TodoInsert from "./components/TodoInsert";
import TodoList from "./components/TodoList";
function App() {
return (
<TodoProvider>
<TodoTemplate>
<TodoInsert />
<TodoList />
</TodoTemplate>
</TodoProvider>
);
}
export default App;
useTodos
훅으로 insertTodo
를 호출합니다.
import React, { useState } from "react";
import { useTodos } from "../TodoContext";
import "./TodoInsert.css";
export default function TodoInsert() {
const [value, setValue] = useState("");
const { insertTodo } = useTodos();
const onSubmit = (e) => {
e.preventDefault();
if (value.trim() === "") return;
insertTodo(value);
setValue("");
};
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="할 일을 입력하세요"
/>
<button type="submit">추가</button>
</form>
);
}
useTodos
훅으로 todos
를 가져옵니다.
import React from "react";
import { useTodos } from "../TodoContext";
import TodoListItem from "./TodoListItem";
import "./TodoList.css";
export default function TodoList() {
const { todos } = useTodos();
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} />
))}
</div>
);
}
useTodos
훅으로 removeTodo
와 toggleTodo
를 가져옵니다.
import React from "react";
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from "react-icons/md";
import { useTodos } from "../TodoContext";
import "./TodoListItem.css";
export default function TodoListItem({ todo }) {
const { id, checked, text } = todo;
const { removeTodo, toggleTodo } = useTodos();
return (
<div className="TodoListItem">
<div
className={checked ? "checkBox checked" : "checkBox"}
onClick={() => toggleTodo(id)}
>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={() => removeTodo(id)}>
<MdRemoveCircleOutline />
</div>
</div>
);
}
이제 todos
상태와 관련된 함수들을 props
로 전달하지 않고,
Context API
를 통해 컴포넌트 전반에서 쉽게 사용할 수 있어!
src
├── App.js
├── TodoContext.js
├── components
│ ├── TodoInsert.js
│ ├── TodoInsert.css
│ ├── TodoList.js
│ ├── TodoList.css
│ ├── TodoListItem.js
│ ├── TodoListItem.css
│ ├── TodoTemplate.js
│ └── TodoTemplate.css
└── index.js
프로젝트 생성
c:\react\my-cra-app> cd c:\react
c:\react> npx create-react-app todo-app
c:\react> cd todo-app
c:\react\todo-app> npm install react@18 react-dom@18
c:\react\todo-app> npm install web-vitals
c:\react\todo-app> npm install react-icons classnames
c:\react\todo-app> code .
c:\react\todo-app> npm start
다시해봤어...ㅠㅠ
물론입니다! 말씀하신 순서에 따라 코드를 작성하겠습니다.
TodoContext.js
src/TodoContext.js
파일을 생성하고 아래 코드를 작성하세요:
// src/TodoContext.js
import { createContext } from "react";
// TodoContext 생성
const TodoContext = createContext();
export default TodoContext;
TodoProvider.js
src/TodoProvider.js
파일을 생성하고 아래 코드를 작성하세요:
// src/TodoProvider.js
import React, { useState, useRef } from "react";
import TodoContext from "./TodoContext";
export default function TodoProvider({ children }) {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: "자바스크립트 공부하기" },
{ id: 2, checked: false, text: "리액트 공부하기" },
{ id: 3, checked: false, text: "할 일 목록 앱 만들기" },
]);
const nextId = useRef(4);
const insertTodo = (text) => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
const removeTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
const toggleTodo = (id) => {
const newTodos = todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
);
setTodos(newTodos);
};
return (
<TodoContext.Provider value={{ todos, insertTodo, removeTodo, toggleTodo }}>
{children}
</TodoContext.Provider>
);
}
App.js
App.js
파일을 수정하여 TodoProvider
로 앱을 감싸줍니다.
// src/App.js
import React from "react";
import TodoProvider from "./TodoProvider";
import TodoTemplate from "./components/TodoTemplate";
import TodoInsert from "./components/TodoInsert";
import TodoList from "./components/TodoList";
function App() {
return (
<TodoProvider>
<TodoTemplate>
<TodoInsert />
<TodoList />
</TodoTemplate>
</TodoProvider>
);
}
export default App;
TodoInsert.js
에서 useContext
로 insertTodo
사용// src/components/TodoInsert.js
import React, { useState, useContext } from "react";
import TodoContext from "../TodoContext";
import { MdAdd } from "react-icons/md";
import "./TodoInsert.css";
export default function TodoInsert() {
const [value, setValue] = useState("");
const { insertTodo } = useContext(TodoContext);
const onSubmit = (e) => {
e.preventDefault();
if (value.trim() === "") return;
insertTodo(value);
setValue("");
};
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="할 일을 입력하세요"
/>
<button type="submit">
<MdAdd />
</button>
</form>
);
}
TodoList.js
에서 useContext
로 todos
사용// src/components/TodoList.js
import React, { useContext } from "react";
import TodoContext from "../TodoContext";
import TodoListItem from "./TodoListItem";
import "./TodoList.css";
export default function TodoList() {
const { todos } = useContext(TodoContext);
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} />
))}
</div>
);
}
TodoListItem.js
에서 useContext
로 removeTodo
, toggleTodo
사용// src/components/TodoListItem.js
import React, { useContext } from "react";
import TodoContext from "../TodoContext";
import {
MdCheckBox,
MdCheckBoxOutlineBlank,
MdRemoveCircleOutline,
} from "react-icons/md";
import "./TodoListItem.css";
export default function TodoListItem({ todo }) {
const { id, checked, text } = todo;
const { removeTodo, toggleTodo } = useContext(TodoContext);
return (
<div className="TodoListItem">
<div
className={checked ? "checkBox checked" : "checkBox"}
onClick={() => toggleTodo(id)}
>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={() => removeTodo(id)}>
<MdRemoveCircleOutline />
</div>
</div>
);
}
src
├── App.js
├── TodoContext.js
├── TodoProvider.js
├── components
│ ├── TodoInsert.js
│ ├── TodoInsert.css
│ ├── TodoList.js
│ ├── TodoList.css
│ ├── TodoListItem.js
│ ├── TodoListItem.css
│ ├── TodoTemplate.js
│ └── TodoTemplate.css
TodoContext.js
: 컨텍스트를 생성.TodoProvider.js
: TodoContext.Provider
를 구현하여 상태와 함수를 제공.App.js
: TodoProvider
로 앱 감싸기.useContext
로 필요한 상태 및 함수 사용.