Wie kann ich den Ref einer zustandslosen Komponente in React anhängen?

77

Ich möchte eine zustandslose Komponente erstellen, deren inputElement von der übergeordneten Komponente überprüft werden kann.

In meinem Beispiel unten stößt ich auf ein Problem, bei dem die Eingabe refniemals dem Privateigentum des Elternteils zugewiesen wird _emailAddress.

Wann handleSubmitheißt, this._emailAddressist undefined. Fehlt mir etwas oder gibt es einen besseren Weg, dies zu tun?

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>
);
Drawwyatt
quelle
Fügen Sie console.logIhrer Ref-Funktion ein hinzu, um zu sehen, ob es aufgerufen wird
Nitzan Tomer
Mögliches Duplikat der reaktionslosen Komponente this.refs..value?
Inanc Gumus

Antworten:

64

EDIT: Sie können jetzt mit React Hooks. Siehe die Antwort von Ante Gulin.

Sie können keinen Zugriff reagieren wie Methoden (wie componentDidMount, componentWillReceivePropsusw.) auf stateless Komponenten, einschließlich refs. Schauen Sie sich diese Diskussion über GH an, um die vollständige Convo zu erhalten.

Die Idee von zustandslos ist, dass keine Instanz dafür erstellt wurde (Status). Daher können Sie kein a anhängen ref, da es keinen Status gibt, an den der ref angehängt werden kann.

Am besten übergeben Sie einen Rückruf, wenn sich die Komponente ändert, und weisen diesen Text dann dem Status der Eltern zu.

Oder Sie können ganz auf die zustandslose Komponente verzichten und eine normale Klassenkomponente verwenden.

Aus den Dokumenten ...

Sie können das ref-Attribut möglicherweise nicht für Funktionskomponenten verwenden, da diese keine Instanzen haben. Sie können jedoch das ref-Attribut in der Renderfunktion einer Funktionskomponente verwenden.

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>
  );  
}
Brad B.
quelle
@jasan verpackst du deine Eingabe in eine andere Komponente? In diesem Fall müssen Sie die Fokusmethode für diese Klasse verfügbar machen. Wenn Sie immer noch ein Problem haben, überprüfen Sie den Wert von textInput in, wenn Sie focus aufrufen.
Brad B
Ja, ich benutze Field aus der Redux-Form. Ich habe die Lösung gefunden. prost
jasan
1
Nun, Sie können immer noch auf die Referenz zugreifen, indem Sie den Wert von der Referenz über eine Requisitenfunktion ref={input => innerRef(input)} an die übergeordnete Komponente übergeben . innerRef={input => this.customInputRef = input}
Eyo Okon Eyo
1
perfekt, danke
Neo
Genau das, wonach ich suche
Laurent
128

Sie können einen useRefHaken verwenden, der seitdem verfügbar ist v16.7.0-alpha.

EDIT: Wir empfehlen Ihnen, Hooks ab 16.8.0Veröffentlichung in der Produktion zu verwenden !

Mit Hooks können Sie den Status beibehalten und Nebenwirkungen in Funktionskomponenten behandeln.

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

Weitere Informationen finden Sie in der Dokumentation zur Hooks-API

Ante Gulin
quelle
3
@Jafarrezaei Ja Hooks sind im Moment in Alpha, werden aber mit Sicherheit übernommen.
Ante Gulin
1
@AnteGulin ja es wird angenommen, aber es ist ALPHA. Das heißt, es ist möglicherweise instabil, ohne Garantie, dass die aktuelle API das Endprodukt erreicht. Es dient ausschließlich zu Test- und Feedbackzwecken, NICHT zur Verwendung in der Produktion.
user2223059
19
Warum bekomme ich eine Ablehnung für die Beantwortung der Frage? Ich habe mich nicht für die Verwendung in der Produktion ausgesprochen und OP auch nicht um Rat gefragt. Die Frage ist, ob Sie Refs in einer zustandslosen Komponente verwenden können und die Antwort lautet JA, Hooks können das.
Ante Gulin
5
Es ist aus Alpha und sogar in reagieren native jetzt 7u7
ValdaXD
Für funktionale Komponenten hat es bei mir nicht funktioniert
Praveen Saboji
2

Der Wert Ihres TextInput ist nichts anderes als ein Status Ihrer Komponente. Anstatt den aktuellen Wert mit einer Referenz abzurufen (schlechte Idee im Allgemeinen, soweit ich weiß), können Sie den aktuellen Status abrufen.

In einer reduzierten Version (ohne Eingabe):

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

quelle
1
Das Zuweisen zum Status wird etwas rau, wenn die Formulare lang werden, weshalb ich versucht habe, dies mit dem zu lösen ref. Ich kaufe mir total, dass das, was ich versuche, hier nicht möglich ist, aber würde es Ihnen etwas ausmachen, einen Artikel zu erklären oder darauf zu verweisen, der erklärt, warum die Verwendung refim Allgemeinen eine schlechte Idee ist?
Drawwyatt
Vielleicht könnten Sie Ihre Komponente in mehreren Ebenen zusammenstellen? Versuchen Sie diese Frage: stackoverflow.com/questions/29503213/…
1
Hier ist Vorsicht geboten: facebook.github.io/react/docs/…
Brad B
2

Das ist spät, aber ich fand diese Lösung viel besser. Achten Sie darauf, wie useRef verwendet wird und wie Eigenschaften unter der aktuellen Eigenschaft verfügbar sind .

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

Für weitere Referenzprüfungen reagieren Sie auf Dokumente

Arun Kumar
quelle
1

Sie können auch Refs in funktionale Komponenten mit ein wenig Sanitär bekommen

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 } />
}
verdverm
quelle