Реализовать интерфейс с автоматическим свойством в TypeScript

Вопрос или проблема

У меня есть компонент React, который действует как контейнер для нескольких текстовых полей. Изначально есть только одно текстовое поле, однако пользователь может динамически добавлять больше.

У меня есть модель для каждого текстового поля:

interface TextFieldProps
{
    value: string,
    setValue: (newValue: string) => void
}

которая используется в моем компоненте MyTextField:

function MyTextField(props: TextFieldProps) => {
    return (<TextField value={props.value} onChange={props.setValue} />)
}

Как видите, MyTextField – это просто обертка вокруг текстового поля MUI. Когда событие onChange на этом текстовом поле срабатывает, выполняется функция setValue, переданная в мою обертку. Таким образом, я могу получать значения текстового поля внутри моего контейнера и также вызывать некоторый код всякий раз, когда одно из текстовых полей изменяется.

Итак, вот компонент Container:

function Container() => {
    const[textFields, setTextFields] = useState<TextFieldProps>[]();

    return(
        <>
            { 
                textFields.map(t => <MyTextField value={ t.value } setValue={ t.setValue } />
            }
        </>
    )
}

Однако у меня нет идеи, как создать экземпляр TextFieldProps, чтобы построить новые компоненты-обертки на основе них.

Что я хочу достичь, так это дать моему контейнеру доступ к значениям текстовых полей и их функциям установки.

Для управления несколькими текстовыми полями одновременно один из способов сделать это — иметь useState, в котором есть карта, где key является уникальным ключом текстового поля, а value — значением ввода.

Затем вы можете использовать свой массив текстовых полей (например, ['firstName', 'lastName', 'address1', 'address2']) и сопоставить MyTextField, получая значение текстового поля по его ключу и функцию установки с помощью фабрики makeHandleChange.

Таким образом, вы можете легко получить доступ к значениям текстовых полей и подстроить фабричную функцию под свою бизнес-логику.

Без фабрики makeHandleChange:

type Values = Record<string, string>;

function Container() => {
  const [textFields, setTextFields] = useState<string[]>([]);
  const [values, setValues] = useState<Values>({});

  const handleChange = (key: string, e: ChangeEvent<HTMLInputElement>) => {
    setValues(prevValues => ({ ...prevValues, [key]: e.target.value }))
  }

  return (<>
    {
      textFields.map(textFieldKey => <MyTextField key={textFieldKey} value={values[textFieldKey} setValue={(e) => handleChange(textFieldKey, e)} />
     } </>
    )
  }

Чтобы достичь этого, вы можете управлять состоянием текстовых полей как массивом объектов в вашем компоненте Container. Каждый объект будет иметь значение и идентификатор, чтобы уникально идентифицировать каждое текстовое поле (например, дата создания, или индекс, или guuid, что угодно), чтобы обеспечить независимое обновление состояния. Пожалуйста, посмотрите на песочницу здесь

Определите компонент текстового поля

Создайте обертку вокруг текста MUI TextField, которая принимает значение и setValue в качестве свойств.

// MyTextField.tsx
import React from 'react';
import { TextField } from '@mui/material';

interface TextFieldProps {
    value: string;
    setValue: (newValue: string) => void;
}

function MyTextField({ value, setValue }: TextFieldProps) {
    return (
        <TextField
            value={value}
            onChange={(e) => setValue(e.target.value)}
            variant="outlined"
            fullWidth
            margin="normal"
        />
    );
}

export default MyTextField;

Определите компонент контейнера

В компоненте контейнера управляйте состоянием как массивом объектов. Каждый объект будет хранить значение и идентификатор.

// Container.tsx
import React, { useState } from 'react';
import MyTextField from './MyTextField';

interface TextFieldData {
    value: string;
    id: number;
}

function Container() {
    // Состояние для хранения массива данных текстовых полей
    const [textFields, setTextFields] = useState<TextFieldData[]>([
        { value: '', id: Date.now() }, // Изначальное текстовое поле
    ]);

    // Функция для добавления нового текстового поля
    const addTextField = () => {
        setTextFields((prevFields) => {
            const updatedFields = [
                ...prevFields,
                { value: '', id: Date.now() }, // Добавьте новое текстовое поле с уникальным ID
            ];
            console.log('После добавления нового поля:', updatedFields); // Лог состояния после добавления поля
            return updatedFields;
        });
    };

    // Функция для обновления значения конкретного текстового поля
    const updateTextField = (id: number, newValue: string) => {
        setTextFields((prevFields) => {
            const updatedFields = prevFields.map((field) =>
                field.id === id ? { ...field, value: newValue } : field
            );
            console.log('После обновления поля:', updatedFields); // Лог состояния после обновления поля
            return updatedFields;
        });
    };

    return (
        <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
            {textFields.map((field) => (
                <MyTextField
                    key={field.id}
                    value={field.value}
                    setValue={(newValue) => updateTextField(field.id, newValue)}
                />
            ))}
            <button onClick={addTextField} style={{ marginTop: '10px' }}>
                Добавить текстовое поле
            </button>
        </div>
    );
}

export default Container;

Объяснение

  1. Состояние (textFields) — это массив объектов, где каждый объект имеет
    значение и уникальный идентификатор. Идентификатор обеспечивает независимое управление каждым текстовым полем.
  2. Функция addTextField добавляет новый объект в массив состояния.
  3. Функция updateTextField обновляет значение конкретного текстового поля
    по его идентификатору.

Ответ или решение

Внедрение интерфейса с автоматическим свойством в TypeScript для управления динамическими текстовыми полями в React

В современном веб-разработке, при создании компонентов на React, нам нередко требуется управлять динамическими элементами, такими как текстовые поля. Изменяя состояние при каждом вводе, мы можем добиться стабильного и отзывчивого интерфейса. В этом руководстве мы рассмотрим, как эффективно организовать управление состоянием множества текстовых полей с помощью интерфейсов и компонентов на TypeScript.

Шаг 1: Определение интерфейса для текстового поля

Сначала необходимо определить интерфейс, который будет описывать структуру свойства текстового поля. Этот интерфейс поможет задать параметры для компонента MyTextField, который является оберткой вокруг текстового поля Material-UI (MUI).

// TextFieldProps.ts
export interface TextFieldProps {
    value: string;
    setValue: (newValue: string) => void;
}

Шаг 2: Создание компонента MyTextField

Теперь создадим простой компонент MyTextField, который будет принимать пропсы, соответствующие нашему интерфейсу. Компонент будет реагировать на события изменения значения текстового поля.

// MyTextField.tsx
import React from 'react';
import TextField from '@mui/material/TextField';
import { TextFieldProps } from './TextFieldProps';

const MyTextField: React.FC<TextFieldProps> = ({ value, setValue }) => {
    return (
        <TextField
            value={value}
            onChange={(e) => setValue(e.target.value)}
            variant="outlined"
            fullWidth
            margin="normal"
        />
    );
};

export default MyTextField;

Шаг 3: Разработка компонента Container

Теперь создадим основной контейнер компонента, который будет управлять состоянием текстовых полей. Исходя из нашего интерфейса, мы будем использовать массив объектов, где каждый объект будет содержать значение текстового поля и уникальный идентификатор.

// Container.tsx
import React, { useState } from 'react';
import MyTextField from './MyTextField';

interface TextFieldData {
    value: string;
    id: number;
}

const Container: React.FC = () => {
    const [textFields, setTextFields] = useState<TextFieldData[]>([
        { value: '', id: Date.now() }, // Изначальное текстовое поле
    ]);

    const addTextField = () => {
        setTextFields((prevFields) => [
            ...prevFields,
            { value: '', id: Date.now() }, // Добавление нового текстового поля
        ]);
    };

    const updateTextField = (id: number, newValue: string) => {
        setTextFields((prevFields) => 
            prevFields.map((field) =>
                field.id === id ? { ...field, value: newValue } : field
            )
        );
    };

    return (
        <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
            {textFields.map((field) => (
                <MyTextField
                    key={field.id}
                    value={field.value}
                    setValue={(newValue) => updateTextField(field.id, newValue)}
                />
            ))}
            <button onClick={addTextField} style={{ marginTop: '10px' }}>
                Добавить текстовое поле
            </button>
        </div>
    );
};

export default Container;

Шаг 4: Пояснение

  1. Структура состояния: Состояние (textFields) представляет собой массив объектов, каждый из которых содержит value и id. Это гарантирует независимое управление состоянием каждого текстового поля.

  2. Добавление текстового поля: Функция addTextField добавляет новый объект в состояние, создавая уникальный идентификатор с помощью Date.now().

  3. Обновление значения текстового поля: Функция updateTextField позволяет обновлять конкретное текстовое поле, используя его уникальный идентификатор.

Заключение

С помощью интеграции интерфейсов в ваше приложение на React, вы можете легко управлять динамическими текстовыми полями, сохраняя чистоту и читабельность кода. Подход, описанный выше, обеспечивает гибкость при управлении состоянием множества текстовых полей, что актуально для приложений с формами и интерактивными элементами.

Создание таких компонентов с использованием TypeScript и React не только улучшает организацию вашего кода, но и повышает его надежность и масштабируемость, что в конечном итоге ведет к повышению качества продукта.

Оцените материал
Добавить комментарий

Капча загружается...