웹사이트 검색

후크를 사용하여 React에서 할 일 목록 앱 구축


기능적 구성요소와 상태 관리를 사용하여 React 앱을 구축하는 방법을 알아보세요.

React는 재사용 가능한 UI 구성 요소를 만들 수 있기 때문에 사용자 인터페이스(UI) 구축을 위한 가장 인기 있고 간단한 JavaScript 라이브러리 중 하나입니다.

React의 구성요소는 애플리케이션의 구성 요소 역할을 하는 독립적이고 재사용 가능한 코드 조각입니다. React 기능 구성 요소는 프레젠테이션 레이어를 비즈니스 로직과 분리하는 JavaScript 기능입니다. React 문서에 따르면 간단한 기능적 구성 요소는 다음과 같이 작성할 수 있습니다.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

React 기능적 구성 요소는 상태가 없습니다. 상태 비저장 구성 요소는 상태가 없고 동일한 props가 주어지면 동일한 마크업을 반환하는 함수로 선언됩니다. 상태는 React 16.8에 도입된 후크가 있는 구성 요소에서 관리됩니다. 이를 통해 기능 구성 요소의 상태 및 수명 주기를 관리할 수 있습니다. 여러 가지 내장 후크가 있으며 사용자 정의 후크를 만들 수도 있습니다.

이 기사에서는 기능적 구성요소와 상태 관리를 사용하여 React에서 간단한 할 일 앱을 구축하는 방법을 설명합니다. 이 앱의 전체 코드는 GitHub 및 CodeSandbox에서 사용할 수 있습니다. 이 튜토리얼을 마치면 앱은 다음과 같이 표시됩니다.

(자이바르단 쿠마르, CC BY-SA 4.0)

전제 조건

  • 로컬로 빌드하려면 Node.js v10.16 이상, Yarn v1.20.0 이상, npm 5.6이 필요합니다.
  • JavaScript에 대한 기본 지식
  • React에 대한 기본적인 이해가 도움이 될 것입니다.

React 앱 만들기

Create React App은 React 앱 구축을 시작할 수 있는 환경입니다. 이 튜토리얼과 함께 정적 유형 정의를 추가하기 위해 TypeScript 템플릿을 사용했습니다. TypeScript는 JavaScript를 기반으로 하는 오픈 소스 언어입니다.

npx create-react-app todo-app-context-api --template typescript

npx는 패키지 실행기 도구입니다. 또는 원사를 사용할 수 있습니다.

yarn create react-app todo-app-context-api --template typescript

이 명령을 실행한 후 디렉터리로 이동하여 앱을 실행할 수 있습니다.

cd todo-app-context-api
yarn start

상용구 코드로 생성된 시작 앱과 React 로고가 표시되어야 합니다. 자신만의 React 앱을 구축하고 있으므로 필요에 맞게 로고와 스타일을 수정할 수 있습니다.

할 일 앱 빌드

할 일 앱은 다음을 수행할 수 있습니다.

  • 항목 추가
  • 목록 항목
  • 항목을 완료됨으로 표시
  • 항목 삭제
  • 상태(예: 완료, 모두, 활성)를 기준으로 항목 필터링

(자이바르단 쿠마르, CC BY-SA 4.0)

헤더 구성 요소

commoders라는 디렉터리를 만들고 Header.tsx라는 파일을 추가합니다.

mkdir components
cd  components
vi  Header.tsx

헤더는 제목을 보유하는 기능적 구성 요소입니다.

const Header: React.FC = () => {
    return (
        <div className="header">
            <h1>
                Add TODO List!!
            </h1>
        </div>
        )
}

AddTodo 구성 요소

AddTodo 구성요소에는 텍스트 상자와 버튼이 포함되어 있습니다. 버튼을 클릭하면 목록에 항목이 추가됩니다.

composites 디렉토리 아래에 todo라는 디렉토리를 만들고 AddTodo.tsx라는 파일을 추가합니다.

mkdir todo
cd todo 
vi AddTodo.tsx

AddTodo는 소품을 허용하는 기능적 구성 요소입니다. Props는 단방향 데이터 전달을 허용합니다. 즉, 상위 구성 요소에서 하위 구성 요소로만 데이터를 전달할 수 있습니다.

const AddTodo: React.FC<AddTodoProps> = ({ todoItem, updateTodoItem, addTaskToList }) => {
    const submitHandler = (event: SyntheticEvent) => {
        event.preventDefault();
        addTaskToList();
    }
    return (
        <form className="addTodoContainer" onSubmit={submitHandler}>
            <div  className="controlContainer">
                <input className="controlSpacing" style={{flex: 1}} type="text" value={todoItem?.text ?? ''} onChange={(ev) => updateTodoItem(ev.target.value)} placeholder="Enter task todo ..." />
                <input className="controlSpacing" style={{flex: 1}} type="submit" value="submit" />
            </div>
            <div>
                <label>
                    <span style={{ color: '#ccc', padding: '20px' }}>{todoItem?.text}</span>
                </label>
            </div>
        </form>
    )
}

상위 함수에서 제공하는 props를 사용하는 AddTodo라는 기능적 React 구성요소를 만들었습니다. 이렇게 하면 구성 요소를 재사용할 수 있습니다. 전달해야 할 소품은 다음과 같습니다.

  • todoItem: 빈 항목 상태
  • updateToDoItem: 사용자가 입력할 때 상위 항목에 콜백을 보내는 도우미 함수
  • addTaskToList: 할 일 목록에 항목을 추가하는 함수

양식, 입력 등과 같은 일부 스타일 및 HTML 요소도 있습니다.

TodoList 구성 요소

다음으로 만들 구성요소는 TodoList입니다. 할 일 상태의 항목을 나열하고 항목을 삭제하고 완료로 표시하는 옵션을 제공하는 일을 담당합니다.

TodoList는 기능적 구성요소입니다.

const TodoList: React.FC = ({ listData, removeItem, toggleItemStatus }) => {
    return listData.length > 0 ? (
        <div className="todoListContainer">
            { listData.map((lData) => {
                return (
                    <ul key={lData.id}>
                        <li>
                            <div className="listItemContainer">
                                <input type="checkbox" style={{ padding: '10px', margin: '5px' }} onChange={() => toggleItemStatus(lData.id)} checked={lData.completed}/>
                                <span className="listItems" style={{ textDecoration: lData.completed ? 'line-through' : 'none', flex: 2 }}>{lData.text}</span>
                                <button type="button" className="listItems" onClick={() => removeItem(lData.id)}>Delete</button>
                            </div>
                        </li>
                    </ul>
                )
            })}
        </div>
    ) : (<span> No Todo list exist </span >)
}

TodoList는 상위 함수의 props를 허용하는 재사용 가능한 기능적 React 구성요소이기도 합니다. 전달해야 할 소품은 다음과 같습니다.

  • listData: ID, 텍스트 및 완료된 속성이 포함된 할 일 목록입니다.
  • removeItem: 할 일 목록에서 항목을 삭제하는 도우미 함수
  • toggleItemStatus: 작업 상태를 완료에서 완료되지 않음으로 또는 그 반대로 전환하는 기능

일부 스타일 및 HTML 요소(목록, 입력 등)도 있습니다.

바닥글 구성요소

바닥글은 기능적 구성요소입니다. 다음과 같이 구성요소 디렉토리에 만듭니다.

cd ..


const Footer: React.FC = ({item = 0, storage, filterTodoList}) => {
    return (
        <div className="footer">
            <button type="button" style={{flex:1}} onClick={() => filterTodoList(ALL_FILTER)}>All Item</button>
            <button type="button" style={{flex:1}} onClick={() => filterTodoList(ACTIVE_FILTER)}>Active</button>
            <button type="button" style={{flex:1}} onClick={() => filterTodoList(COMPLETED_FILTER)}>Completed</button>
            <span style={{color: '#cecece', flex:4, textAlign: 'center'}}>{item} Items | Make use of {storage} to store data</span>
        </div>
    );
}

세 가지 소품을 허용합니다:

  • 항목: 항목 수를 표시합니다.
  • 저장소: 텍스트를 표시합니다.
  • filterTodoList: 상태(활성, 완료, 모든 항목)에 따라 작업을 필터링하는 기능

Todo 구성요소: contextApi 및 useReducer를 사용하여 상태 관리

(자이바르단 쿠마르, CC BY-SA 4.0)

컨텍스트는 모든 수준에서 수동으로 소품을 전달할 필요 없이 구성 요소 트리를 통해 데이터를 전달하는 방법을 제공합니다. ContextApi 및 useReducer는 상태를 트리의 각 구성 요소에 prop으로 전달하지 않고 전체 React 구성 요소 트리에서 공유하여 상태를 관리하는 데 사용할 수 있습니다.

이제 AddTodo, TodoList 및 Footer 구성 요소가 있으므로 연결해야 합니다.

구성 요소의 상태와 수명 주기를 관리하려면 다음 기본 제공 후크를 사용하세요.

  • useState: 상태를 업데이트하는 상태 저장 값과 업데이트 기능을 반환합니다.
  • useEffect: 기능적 구성 요소의 수명 주기를 관리하고 부작용을 수행하는 데 도움이 됩니다.
  • useContext: 컨텍스트 개체를 허용하고 현재 컨텍스트 값을 반환합니다.
  • useReducer: useState와 마찬가지로 상태 저장 값과 업데이트 함수를 반환하지만 복잡한 상태 로직이 있는 경우(예: 여러 하위 값이 있거나 새 상태가 이전 상태에 종속되는 경우) useState 대신 사용됩니다. 하나)

먼저 contextApi 및 useReducer 후크를 사용하여 상태를 관리합니다. 문제를 분리하려면 contextApiComponents라는 구성 요소 아래에 새 디렉터리를 추가하세요.

mkdir contextApiComponents
cd contextApiComponents

TodoContextApi.tsx를 만듭니다.

const defaultTodoItem: TodoItemProp = { id: Date.now(), text: '', completed: false };

const TodoContextApi: React.FC = () => {
    const { state: { todoList }, dispatch } = React.useContext(TodoContext);
    const [todoItem, setTodoItem] = React.useState(defaultTodoItem);
    const [todoListData, setTodoListData] = React.useState(todoList);

    React.useEffect(() => {
        setTodoListData(todoList);
    }, [todoList])

    const updateTodoItem = (text: string) => {
        setTodoItem({
            id: Date.now(),
            text,
            completed: false
        })
    }
    const addTaskToList = () => {
        dispatch({
            type: ADD_TODO_ACTION,
            payload: todoItem
        });
        setTodoItem(defaultTodoItem);
    }
    const removeItem = (id: number) => {
        dispatch({
            type: REMOVE_TODO_ACTION,
            payload: { id }
        })
    }
    const toggleItemStatus = (id: number) => {
        dispatch({
            type: UPDATE_TODO_ACTION,
            payload: { id }
        })
    }
    const filterTodoList = (type: string) => {
        const filteredList = FilterReducer(todoList, {type});
        setTodoListData(filteredList)

    }

    return (
        <>
            <AddTodo todoItem={todoItem} updateTodoItem={updateTodoItem} addTaskToList={addTaskToList} />
            <TodoList listData={todoListData} removeItem={removeItem} toggleItemStatus={toggleItemStatus} />
            <Footer item={todoListData.length} storage="Context API" filterTodoList={filterTodoList} />
        </>
    )
}

이 구성요소에는 AddTodo, TodoList 및 바닥글 구성요소와 해당 도우미 및 콜백 함수가 포함됩니다.

상태를 관리하기 위해 상태 및 디스패치 메서드를 제공하고 상태를 업데이트하는 contextApi를 사용합니다. 컨텍스트 개체를 허용합니다. (다음에는 contextProvider라는 컨텍스트 제공자를 만듭니다).

 const { state: { todoList }, dispatch } = React.useContext(TodoContext);

Todo 제공자

컨텍스트를 생성하고 useReducer 후크를 사용하는 TodoProvider를 추가합니다. useReducer 후크는 초기 값과 함께 감속기 함수를 사용하고 상태 및 업데이트 함수(디스패치)를 반환합니다.

  • 컨텍스트를 생성하고 내보냅니다. 내보내면 모든 하위 구성요소에서 useContext 후크를 사용하여 현재 상태를 가져오는 데 사용할 수 있습니다.

    export const TodoContext = React.createContext({} as TodoContextProps);
  • ContextProvider를 생성하고 내보냅니다.

    const TodoProvider : React.FC = (props) => {
        const [state, dispatch] = React.useReducer(TodoReducer, {todoList: []});
        const value = {state, dispatch}
        return (
            <TodoContext.Provider value={value}>
                {props.children}
            </TodoContext.Provider>
        )
    }
  • 상위 구성 요소(예: TodoContextApi) 또는 앱 자체를 공급자( 예: TodoProvider):

    <TodoProvider>
      <TodoContextApi />
    </TodoProvider>
  • TodoContextApi 구성요소에서 useContext 후크를 사용하여 현재 컨텍스트 값에 액세스합니다.

    const { state: { todoList }, dispatch } = React.useContext(TodoContext)

TodoProvider.tsx:

type TodoContextProps = {
    state : {todoList: TodoItemProp[]};
    dispatch: ({type, payload}: {type:string, payload: any}) => void;
}

export const TodoContext = React.createContext({} as TodoContextProps);

const TodoProvider : React.FC = (props) => {
    const [state, dispatch] = React.useReducer(TodoReducer, {todoList: []});
    const value = {state, dispatch}
    return (
        <TodoContext.Provider value={value}>
            {props.children}
        </TodoContext.Provider>
    )
}

감속기

리듀서는 부작용이 없는 순수한 함수입니다. 이는 동일한 입력에 대해 예상되는 출력이 항상 동일하다는 것을 의미합니다. 이렇게 하면 리듀서를 독립적으로 테스트하기가 더 쉬워지고 상태를 관리하는 데 도움이 됩니다. TodoReducer 및 FilterReducer는 TodoProvider 및 TodoContextApi 구성요소에서 사용됩니다.

src 아래에 reducers라는 디렉터리를 만들고 여기에 TodoReducer.tsx라는 파일을 만듭니다.

const TodoReducer = (state: StateProps = {todoList:[]}, action: ActionProps) => {
    switch(action.type) {
        case ADD_TODO_ACTION:
            return { todoList: [...state.todoList, action.payload]}
        case REMOVE_TODO_ACTION:
            return { todoList: state.todoList.length ? state.todoList.filter((d) => d.id !== action.payload.id) : []};
        case UPDATE_TODO_ACTION:
            return { todoList: state.todoList.length ? state.todoList.map((d) => {
                if(d.id === action.payload.id) d.completed = !d.completed;
                return d;
            }): []}
        default:
            return state;
    }
}

필터 상태를 유지하려면 FilterReducer를 만듭니다.

const FilterReducer =(state : TodoItemProp[] = [], action: ActionProps) => {
    switch(action.type) {
        case ALL_FILTER:
            return state;
        case ACTIVE_FILTER:
            return state.filter((d) => !d.completed);
        case COMPLETED_FILTER:
            return state.filter((d) => d.completed);
        default:
            return state;
    }
}

필요한 모든 구성요소를 생성했습니다. 다음으로, 앱에 Header 및 TodoContextApi 구성요소를 추가하고 TodoProvider와 함께 TodoContextApi를 추가하여 모든 어린이가 컨텍스트에 액세스합니다.

function App() {
  return (
    <div className="App">
      <Header />
      <TodoProvider>
              <TodoContextApi />
      </TodoProvider>
    </div>
  );
}

앱 구성요소가 ReactDom.render 내의 index.tsx에 있는지 확인하세요. ReactDom.render는 React Element와 HTML 요소의 ID라는 두 가지 인수를 사용합니다. React 요소는 웹페이지에 렌더링되며 id는 React 요소로 대체될 HTML 요소를 나타냅니다.

ReactDOM.render(
   <App />,
  document.getElementById('root')
);

결론

후크와 상태 관리를 사용하여 React에서 기능적인 앱을 구축하는 방법을 배웠습니다. 그걸로 무엇을 할 건가요?

관련기사: