Поиск по сайту:

Создайте приложение списка дел в React с помощью хуков


Научитесь создавать приложения React, используя функциональные компоненты и управление состоянием.

React — одна из самых популярных и простых библиотек JavaScript для создания пользовательских интерфейсов (UI), поскольку она позволяет создавать повторно используемые компоненты пользовательского интерфейса.

Компоненты в React — это независимые, многократно используемые фрагменты кода, которые служат строительными блоками приложения. Функциональные компоненты React — это функции JavaScript, которые отделяют уровень представления от бизнес-логики. Согласно документации React, простой функциональный компонент можно написать так:

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

Функциональные компоненты React не имеют состояния. Компоненты без состояния объявляются как функции, которые не имеют состояния и возвращают одну и ту же разметку с одинаковыми реквизитами. Состояние управляется в компонентах с помощью хуков, которые были представлены в React 16.8. Они позволяют управлять состоянием и жизненным циклом функциональных компонентов. Существует несколько встроенных хуков, но вы также можете создавать собственные хуки.

В этой статье объясняется, как создать простое приложение для дел в React, используя функциональные компоненты и управление состоянием. Полный код этого приложения доступен на GitHub и CodeSandbox. Когда вы закончите изучение этого руководства, приложение будет выглядеть так:

(Джайвардхан Кумар, CC BY-SA 4.0)

Предварительные условия

  • Для локальной сборки у вас должен быть Node.js 10.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)

Компонент заголовка

Создайте каталог с именем компоненты и добавьте файл с именем 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 содержит текстовое поле и кнопку. Нажатие кнопки добавляет элемент в список.

Создайте каталог с именем todo в каталоге comments и добавьте файл с именем AddTodo.tsx:

mkdir todo
cd todo 
vi AddTodo.tsx

AddTodo — функциональный компонент, принимающий реквизиты. Пропсы допускают одностороннюю передачу данных, т. е. только от родительского компонента к дочернему:

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>
    )
}

Вы создали функциональный компонент React под названием AddTodo, который принимает реквизиты, предоставленные родительской функцией. Это делает компонент многоразовым. Реквизиты, которые необходимо передать:

  • 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 также является функциональным компонентом React многократного использования, который принимает реквизиты из родительских функций. Реквизиты, которые необходимо передать:

  • listData: список задач с идентификаторами, текстом и завершенными свойствами.
  • removeItem: вспомогательная функция для удаления элемента из списка дел.
  • toggleItemStatus: функция переключения статуса задачи с выполненного на невыполненное и наоборот.

Также есть некоторые стили и элементы HTML (например, списки, ввод и т. д.).

Компонент нижнего колонтитула

Нижний колонтитул будет функциональным компонентом; создайте его в каталоге comments следующим образом:

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>
    );
}

Он принимает три реквизита:

  • item: отображает количество элементов.
  • хранилище: отображает текст.
  • filterTodoList: функция для фильтрации задач по статусу (активно, завершено, все элементы).

Компонент Todo: управление состоянием с помощью contextApi и useReducer

(Джайвардхан Кумар, CC BY-SA 4.0)

Контекст предоставляет способ передавать данные через дерево компонентов без необходимости передавать реквизиты вручную на каждом уровне. ContextApi и useReducer можно использовать для управления состоянием, разделяя его по всему дереву компонентов 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 и Footer, а также соответствующие им вспомогательные функции и функции обратного вызова.

Для управления состоянием он использует contextApi, который предоставляет методы состояния и отправки, которые, в свою очередь, обновляют состояние. Он принимает объект контекста. (Далее вы создадите поставщика контекста под названием contextProvider).

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

ТодоПровайдер

Добавьте TodoProvider, который создает контекст и использует перехватчик useReducer. Хук 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>
        )
    }
  • Доступ к данным контекста может быть доступен любому компоненту React в иерархии напрямую с помощью хука useContext, если вы обертываете родительский компонент (например, 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.

Создайте каталог с именем reducers в src и создайте там файл с именем 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 в App, а также TodoContextApi с TodoProvider, чтобы все дочерние элементы могли получить доступ к контексту.

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

Убедитесь, что компонент приложения находится в index.tsx внутри ReactDom.render. ReactDom.render принимает два аргумента: элемент React и идентификатор элемента HTML. Элемент React отображается на веб-странице, а id указывает, какой элемент HTML будет заменен элементом React:

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

Заключение

Вы узнали, как создать функциональное приложение в React, используя перехватчики и управление состоянием. Что ты будешь с этим делать?

Статьи по данной тематике: