Як я можу приєднати посилання на компонент без стану у React?


79

Я прагну створити компонент без стану, inputелемент якого може перевіряти батьківський компонент.

У наведеному нижче прикладі я стикаюся з проблемою, коли введення refніколи не призначається приватній _emailAddressвласності батьків .

Коли handleSubmitвикликається, this._emailAddressє undefined. Щось мені не вистачає, чи є кращий спосіб це зробити?

interface FormTestState {
    errors: string;
}

class FormTest extends React.Component<void, FormTestState> {
    componentWillMount() {
        this.setState({ errors: '' });
    }

    render(): JSX.Element {
        return (
            <main role='main' className='about_us'>             
                <form onSubmit={this._handleSubmit.bind(this)}>
                    <TextInput 
                        label='email'
                        inputName='txtInput'
                        ariaLabel='email'
                        validation={this.state.errors}
                        ref={r => this._emailAddress = r}
                    />

                    <button type='submit'>submit</button>
                </form>
            </main>
        );
    }

    private _emailAddress: HTMLInputElement;

    private _handleSubmit(event: Event): void {
        event.preventDefault();
        // this._emailAddress is undefined
        if (!Validators.isEmail(this._emailAddress.value)) {
            this.setState({ errors: 'Please enter an email address.' });
        } else {
            this.setState({ errors: 'All Good.' });
        }
    }
}

const TextInput = ({ label, inputName, ariaLabel, validation, ref }: { label: string; inputName: string; ariaLabel: string; validation?: string; ref: (ref: HTMLInputElement) => void }) => (
    <div>
        <label htmlFor='txt_register_first_name'>
            { label }
        </label>

        <input type='text' id={inputName} name={inputName} className='input ' aria-label={ariaLabel} ref={ref} />

        <div className='input_validation'>
            <span>{validation}</span>
        </div>
    </div>
);

Додайте a console.logдо своєї функції ref, щоб побачити, чи викликається вона
Nitzan Tomer

Можливий дублікат компоненту React без стану this.refs..value?
Inanc Gumus

Відповіді:


63

EDIT: Тепер ви можете з React Hooks. Дивіться відповідь Анте Гуліна.

Ви не можете доступу Реагувати як методи (наприклад componentDidMount, componentWillReceivePropsі т.д.) на компоненти без громадянства, в тому числі refs. Ознайомтесь із цією дискусією щодо GH, щоб отримати повний конвой.

Ідея апатрида полягає в тому, що для нього не створено екземпляра (стану). Таким чином, ви не можете приєднати a ref, оскільки немає жодного стану, до якого можна додати посилання.

Найкращим варіантом було б передати зворотний виклик, коли компонент змінюється, а потім призначити цей текст батьківському стану.

Або ви можете взагалі відмовитись від компонента без громадянства та використовувати звичайний компонент класу.

З документів ...

Ви не можете використовувати атрибут ref для функціональних компонентів, оскільки вони не мають екземплярів. Однак ви можете використовувати атрибут ref всередині функції візуалізації функціонального компонента.

function CustomTextInput(props) {
  // textInput must be declared here so the ref callback can refer to it
  let textInput = null;

  function handleClick() {
    textInput.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

@jasan, ти обгортаєш свої дані в інший компонент? якщо так, то вам потрібно буде виставити метод focus на цей клас. Якщо проблема все ще виникає, перевірте значення textInput, коли ви телефонуєте фокусом.
Бред Б,

так, я використовую поле з redux-форми. Я знайшов рішення. ура
Jasan

1
Ну, ви все ще можете отримати доступ до ref, передавши значення від ref батьківському за допомогою функції prop, у компоненті без стану ref={input => innerRef(input)} , для батьків, використовуйте переданий вами проп, як зазвичай використовуєте ref; innerRef={input => this.customInputRef = input}
Eyo Okon Eyo

1
ця відповідь застаріла, оскількиv16.8.0
Себастьян

132

Ви можете використовувати useRefгачок, який доступний з v16.7.0-alpha.

EDIT: Вам рекомендується використовувати Хуки у виробництві з 16.8.0моменту випуску!

Гачки дозволяють підтримувати стан та обробляти побічні ефекти у функціональних компонентах.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Детальніше читайте в документації API Hooks


3
@Jafarrezaei Так, Гаки на даний момент знаходяться в альфа-версії, але, безумовно, будуть прийняті.
Анте Гулін

1
@AnteGulin так, це буде прийнято, але це АЛЬФА. Це означає, що він, можливо, нестабільний, без жодних гарантій, що поточний API буде тим, що потрапляє до кінцевого продукту. Він існує виключно для тестування та зворотного зв'язку, а не для виробничого використання.
user2223059

5
Він вийшов з альфи і навіть реагує рідним зараз 7u7
ValdaXD

4

Це пізно, але я знайшов це рішення набагато кращим. Зверніть увагу на те, як він використовує useRef та як властивості доступні в поточній власності.

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

Для отримання додаткової довідки перевірте docs


2

Значення вашого TextInput - це не що інше, як стан вашого компонента. Отже, замість отримання поточного значення за допомогою посилання (погана ідея загалом, наскільки я знаю), ви можете отримати поточний стан.

У зменшеній версії (без набору тексту):

class Form extends React.Component {
  constructor() {
    this.state = { _emailAddress: '' };

    this.updateEmailAddress = this.updateEmailAddress.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  updateEmailAddress(e) {
    this.setState({ _emailAddress: e.target.value });
  }

  handleSubmit() {
    console.log(this.state._emailAddress);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          value={this.state._emailAddress}
          onChange={this.updateEmailAddress}
        />
      </form>
    );
  }
}

1
Присвоєння стану стає трохи грубим, коли форми затягуються, саме тому я намагався вирішити це за допомогою ref. Я повністю купую, що те, що я намагаюся зробити, тут неможливо, але ви не могли б пояснити або посилатися на статтю, яка пояснює, чому використання ref- це погана ідея взагалі?
drewwyatt

Можливо, ви могли б скласти свій компонент на декількох рівнях? Спробуйте це питання: stackoverflow.com/questions/29503213 / ...

1
Тут є слово обережності: facebook.github.io/react/docs/…
Бред Б,

1

Ви також можете отримати посилання на функціональні компоненти з невеликою кількістю сантехніки

import React, { useEffect, useRef } from 'react';

// Main functional, complex component
const Canvas = (props) => {
  const canvasRef = useRef(null);

    // Canvas State
  const [canvasState, setCanvasState] = useState({
      stage: null,
      layer: null,
      context: null,
      canvas: null,
      image: null
  });

  useEffect(() => {
    canvasRef.current = canvasState;
    props.getRef(canvasRef);
  }, [canvasState]);


  // Initialize canvas
  useEffect(() => {
    setupCanvas();
  }, []);

  // ... I'm using this for a Konva canvas with external controls ...

  return (<div>...</div>);
}

// Toolbar which can do things to the canvas
const Toolbar = (props) => {
  console.log("Toolbar", props.canvasRef)

  // ...
}

// Parent which collects the ref from Canvas and passes to Toolbar
const CanvasView = (props) => {
  const canvasRef = useRef(null);

  return (
    <Toolbar canvasRef={canvasRef} />
    <Canvas getRef={ ref => canvasRef.current = ref.current } />
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.