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

IT技术 reactjs typescript jsx
2021-04-01 17:05:10

我希望创建一个无状态组件,其input元素可以由父组件验证。

在下面的示例中,我遇到了一个问题,即输入ref永远不会分配给父级的私有_emailAddress财产。

handleSubmit被调用时,this._emailAddressundefined有什么我遗漏的,还是有更好的方法来做到这一点?

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>
);
5个回答

您可以使用useRefv16.7.0-alpha.

编辑:鼓励您在16.8.0发布时在生产中使用 Hooks

钩子使您能够在功能组件中维护状态并处理副作用。

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 文档中阅读更多内容

@AnteGulin 是的,它将被采用,但它是 ALPHA。这意味着它可能不稳定,无法保证当前的 API 将成为最终产品。它的存在纯粹是为了测试和反馈,而不是为了生产使用。
2021-05-25 17:05:10
它超出了 alpha 版本,甚至现在是 react native 7u7
2021-05-31 17:05:10
@Jafarrezaei 是的 Hooks 目前处于 alpha 阶段,但肯定会被采用。
2021-06-08 17:05:10

编辑:您现在可以使用 React Hooks。见安特古林的回答。

您无法访问做出react等的方法(例如componentDidMountcomponentWillReceiveProps在无状态组件等)在内refs查看这个关于 GH 的讨论以获得完整的会议。

无状态的想法是没有为其创建实例(状态)。因此,您不能附加 a ref,因为没有要附加 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>
  );  
}
这个答案已经过时了,因为 v16.8.0
2021-05-25 17:05:10
好吧,您仍然可以通过将值从 ref 传递给父级来访问 ref,通过 prop 函数,在无状态组件 do 上 ref={input => innerRef(input)} ,在父级上使用您传递的 prop,就像您通常使用 ref 一样; innerRef={input => this.customInputRef = input}
2021-05-29 17:05:10
当我不想在页面加载后立即使用 textInput 时怎么样?如何在功能组件中执行 componentDidMount ?我确实使用了`` useEffect(x=>{ //use this now textInput; 但它仍然是 null... },[])``
2021-06-09 17:05:10
是的,我正在使用 redux-form 中的 Field。我找到了解决方案。干杯
2021-06-12 17:05:10
@jasan 您是否将输入包装在另一个组件中?如果是这样,您需要在该类上公开 focus 方法。如果您仍然遇到问题,请在调用焦点时检查 textInput 的值。
2021-06-14 17:05:10

这已经很晚了,但我发现这个解决方案要好得多。注意它如何使用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>
  );
}

有关更多参考检查react文档

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>
    );
  }
}
这里有一个警告:facebook.github.io/react/docs/...
2021-06-03 17:05:10
当表单变得冗长时,分配给状态会变得有点粗糙,这就是为什么我试图用ref. 我完全相信我正在尝试做的事情在这里是不可能的,但是你介意解释一下,或者参考一篇解释为什么使用ref是一个坏主意的文章吗?
2021-06-11 17:05:10
也许你可以在多个层次上组合你的组件?试试这个问题:stackoverflow.com/questions/29503213/...
2021-06-17 17:05:10

您还可以通过一些管道将 refs 导入到功能组件中

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 } />
}
你在哪里定义了 Canvas 组件中的 getRef ?
2021-06-15 17:05:10