Как я могу прикрепить ссылку на компонент без состояния в React?

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

В моем примере ниже я сталкиваюсь с проблемой, когда входной 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>
);

3 ответа

Решение

Вы не можете получить доступ к React как методы (например, componentDidMount, componentWillReceivePropsи т. д.) на компоненты без состояния, включая refs, Проверьте это обсуждение на GH для полного convo.

Идея безгражданства состоит в том, что для него не создается экземпляр (состояние). Таким образом, вы не можете прикрепить 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>
  );  
}

Ты можешь использовать useRef крюк, который доступен с v16.7.0-alpha,

РЕДАКТИРОВАТЬ: вам рекомендуется использовать крючки в производстве с 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>
    </>
  );
}

Подробнее читайте в документации по Hooks API

Это поздно, но я нашел это решение намного лучше. Обратите внимание на то, как он использует 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>
  );
}

Для получения дополнительной справки проверьте реагирующие документы

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

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

  function handleClick() {
    validationHandler(textInput.value);
  }

  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Validate the text input from a parent function"
        onClick={handleClick}
      />
    </div>
  );  
}

Значение вашего 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>
    );
  }
}

Вы также можете получить ссылки на функциональные компоненты с небольшой сантехникой

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 } />
}
Другие вопросы по тегам