如何在React中附加无状态组件的引用?

时间:2022-08-22 20:21:58

I am looking to create a stateless component who's input can be validated by the parent element.

我期待创建一个无状态组件,其输入可以由父元素验证。

In my example below, I am running into a problem where the input ref is never being assigned to the parent's private _emailAddress property.

在下面的示例中,我遇到了一个问题,即输入引用永远不会被分配给父的私有_emailAddress属性。

When handleSubmit is called, this._emailAddress is undefined. Is there something I'm missing, or is there a better way to do this?

调用handleSubmit时,this._emailAddress未定义。有什么我想念的,还是有更好的方法来做到这一点?

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 个解决方案

#1


20  

You can't access React like methods (like componentDidMount, componentWillReceiveProps, etc) on stateless components, including refs. Checkout this discussion on GH for the full convo.

您无法在无状态组件(包括refs)*问类似React的方法(如componentDidMount,componentWillReceiveProps等)。查看关于GH的全部讨论的讨论。

The idea of stateless is that there isn't an instance created for it (state). As such, you can't attach a ref, since there's no state to attach the ref to.

无状态的想法是没有为它创建实例(状态)。因此,您无法附加引用,因为没有附加引用的状态。

Your best bet would be to pass in a callback for when the component changes and then assign that text to the parent's state.

您最好的选择是传递组件更改时的回调,然后将该文本分配给父级的状态。

Or, you can forego the stateless component altogether and use an normal class component.

或者,您可以完全放弃无状态组件并使用普通的类组件。

From the docs...

来自文档......

You may not use the ref attribute on functional components because they don't have instances. You can, however, use the ref attribute inside the render function of a functional component.

您可能不会在功能组件上使用ref属性,因为它们没有实例。但是,您可以在功能组件的render函数中使用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>
  );  
}

#2


2  

I needed something similar to this. To add to Brad's answer, you can pass a validation handler as a prop and then use the inner function to invoke the validation handler.

我需要类似的东西。要添加到Brad的答案,您可以将验证处理程序作为prop传递,然后使用inner函数来调用验证处理程序。

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

#3


1  

The value of your TextInput is nothing more than a state of your component. So instead of fetching the current value with a reference (bad idea in general, as far as I know) you could fetch the current state.

TextInput的值只不过是组件的状态。因此,不是通过引用获取当前值(一般来说是个坏主意,据我所知),您可以获取当前状态。

In a reduced version (without typing):

在缩减版本中(无需输入):

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


20  

You can't access React like methods (like componentDidMount, componentWillReceiveProps, etc) on stateless components, including refs. Checkout this discussion on GH for the full convo.

您无法在无状态组件(包括refs)*问类似React的方法(如componentDidMount,componentWillReceiveProps等)。查看关于GH的全部讨论的讨论。

The idea of stateless is that there isn't an instance created for it (state). As such, you can't attach a ref, since there's no state to attach the ref to.

无状态的想法是没有为它创建实例(状态)。因此,您无法附加引用,因为没有附加引用的状态。

Your best bet would be to pass in a callback for when the component changes and then assign that text to the parent's state.

您最好的选择是传递组件更改时的回调,然后将该文本分配给父级的状态。

Or, you can forego the stateless component altogether and use an normal class component.

或者,您可以完全放弃无状态组件并使用普通的类组件。

From the docs...

来自文档......

You may not use the ref attribute on functional components because they don't have instances. You can, however, use the ref attribute inside the render function of a functional component.

您可能不会在功能组件上使用ref属性,因为它们没有实例。但是,您可以在功能组件的render函数中使用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>
  );  
}

#2


2  

I needed something similar to this. To add to Brad's answer, you can pass a validation handler as a prop and then use the inner function to invoke the validation handler.

我需要类似的东西。要添加到Brad的答案,您可以将验证处理程序作为prop传递,然后使用inner函数来调用验证处理程序。

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

#3


1  

The value of your TextInput is nothing more than a state of your component. So instead of fetching the current value with a reference (bad idea in general, as far as I know) you could fetch the current state.

TextInput的值只不过是组件的状态。因此,不是通过引用获取当前值(一般来说是个坏主意,据我所知),您可以获取当前状态。

In a reduced version (without typing):

在缩减版本中(无需输入):

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