Переопределить входное значение в дочернем компоненте в React

У меня есть дочерний компонент ввода и родитель, где я устанавливаю значение.

Родительский компонент

const \[dummyText, setDummyText\] = useState('')
return (

<Input
  {...props}
  id = "first-name"
  label = "First name"
  type = "text"
  placeholder = "Please enter your first name"
  value = {dummyText}
  onChange = {(value) => setDummyText(value.target.value)}
/>
)

Дочерний компонент ввода

    import React, { InputHTMLAttributes, forwardRef } from 'react'
    export type Props = {} & InputHTMLAttributes<HTMLInputElement>

    const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Partial<Props>>(
      ({ ...props }, ref) => <input ref = {ref} {...props} />
    )

    FormInput.displayName = 'FormInput'

    export default FormInput

Я хочу добавить кнопку в дочерний компонент, где при нажатии мне нужно очистить значение.

Дело в том, что я хочу иметь четкую функциональность в дочернем компоненте и не хочу писать лишний код для каждого <Input в родительском, и что бы я ни делал в дочернем компоненте, я просто не могу переопределить props.value.

У кого-нибудь есть опыт, как это решить?

Не могли бы вы поделиться своим компонентом Input?

RubenSmn 04.02.2023 13:30

@RubenSmn Я обновил свой код. Tnx

xapep 04.02.2023 15:09
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
2
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы хотите очистить входное значение, вы можете передать функцию очистки в качестве реквизита дочернему компоненту следующим образом:

const Input = ({ onClearInput, ...otherProps }) => {
  return (
    <div style = {{ display: 'flex', gap: '.5rem' }}>
      <input {...otherProps} />
      <button onClick = {onClearInput}>Clear</button>
    </div>
  );
};

export default function App() {
  const [firstName, setFirstName] = useState('');
  const [secondName, setSecondName] = useState('');

  const onClearInputHandler = (handler) => handler('');

  return (
    <div style = {{ display: 'flex', flexDirection: 'column', gap: '.5rem' }}>
      <Input
        id = "first-name"
        label = "First name"
        type = "text"
        placeholder = "Please enter your first name"
        value = {firstName}
        onChange = {(event) => setFirstName(event.target.value)}
        onClearInput = {() => onClearInputHandler(setFirstName)}
      />

      <Input
        id = "second-name"
        label = "Second name"
        type = "text"
        placeholder = "Please enter your second name"
        value = {secondName}
        onChange = {(event) => setSecondName(event.target.value)}
        onClearInput = {() => onClearInputHandler(setSecondName)}
      />
    </div>
  );
}

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

Редактировать: Тогда я думаю, что самое простое решение — обработать функцию clear внутри самого ввода следующим образом:

  const Input = (props) => {
  const [value, setValue] = useState(props.value || '');
  return (
    <div style = {{ display: 'flex', gap: '.5rem' }}>
      <input
        {...props}
        value = {value}
        onChange = {(event) => setValue(event.target.value)}
      />
      <button onClick = {() => setValue('')}>Clear</button>
    </div>
  );
};

export default function App() {
  const onSubmitHandler = (event) => {
    event.preventDefault();

    const formData = new FormData(event.target);
    const formProps = Object.fromEntries(formData);
    console.info(formProps);
  };

  return (
    <form action = "submit" onSubmit = {onSubmitHandler}>
      <div style = {{ display: 'flex', flexDirection: 'column', gap: '.5rem' }}>
        <Input
          id = "first-name"
          label = "First name"
          type = "text"
          placeholder = "Please enter your first name"
          name = "first-name"
        />

        <Input
          id = "second-name"
          label = "Second name"
          type = "text"
          placeholder = "Please enter your second name"
          name = "second-name"
        />
      </div>
      <button type = "submit" style = {{ margin: '.5rem 0' }}>
        Submit
      </button>
    </form>
  );

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

Вот демо на stackblitz: https://stackblitz.com/edit/react-gnyvav?file=src%2FApp.js

Спасибо за усилия jcobo1. Дело в том, что, как я уже сказал, я хочу иметь четкую функцию внутри дочернего компонента, которая переопределяет props.value. Я хочу избежать написания родительской функции для каждого использования <Input, поскольку очистка является частью базовой «настройки» нашего компонента <Input.

xapep 04.02.2023 12:04

Для этого есть несколько решений, я придумал эти два

onValueChange

Другим решением может быть преобразование onChange в onValueChange, которое вместо event немедленно возвращает value. Обновил тип и удалил Partial, так как он выдавал некоторые ошибки.

type Props = {
  onValueChange: (value: string) => void;
} & InputHTMLAttributes<HTMLInputElement>;

const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Props>(
  ({ onValueChange, ...props }, ref) => {
    return (
      <>
        <input
          ref = {ref}
          onChange = {(e) => onValueChange(e.target.value)}
          {...props}
        />
        <button onClick = {() => onValueChange("")}>clear</button>
      </>
    );
  }
);

Проблема в том, что если указан onChange, он переопределит существующий onChange. Мы можем решить эту проблему, просто добавив его и передав e (событие)

const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Props>(
  ({ onValueChange, onChange, ...props }, ref) => {
    return (
      <>
        <input
          ref = {ref}
          onChange = {(e) => {
            onValueChange(e.target.value);
            onChange(e);
          }}
          {...props}
        />
        <button onClick = {() => onValueChange("")}>clear</button>
      </>
    );
  }
);

Который вы затем можете использовать

<FormInput
  id = "first-name"
  type = "text"
  placeholder = "Please enter your first name"
  value = {dummyText}
  onValueChange = {setDummyText}
  // onChange = {(e) => console.info(e)}
/>

onClear

Теперь это не полностью перемещает функциональность в компонент ввода.

Мы деструктурируем функцию onClear и добавляем ее в onClick кнопки очистки.

type Props = { onClear: () => void } & InputHTMLAttributes<HTMLInputElement>;

const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Props>(
  ({ onClear, ...props }, ref) => {
    return (
      <>
        <input ref = {ref} {...props} />
        <button onClick = {onClear}>clear</button>
      </>
    );
  }
);

Теперь, чтобы использовать его, вы можете пойти так

<Input
  id = "first-name"
  onClear = {() => setDummyText("")}
  type = "text"
  placeholder = "Please enter your first name"
  value = {dummyText}
  onChange = {(event) => setDummyText(event.target.value)}
/>

Другие вопросы по теме