React: "this" non è definito all'interno di una funzione componente


152
class PlayerControls extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      loopActive: false,
      shuffleActive: false,
    }
  }

  render() {
    var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"

    return (
      <div className="player-controls">
        <FontAwesome
          className="player-control-icon"
          name='refresh'
          onClick={this.onToggleLoop}
          spin={this.state.loopActive}
        />
        <FontAwesome
          className={shuffleClassName}
          name='random'
          onClick={this.onToggleShuffle}
        />
      </div>
    );
  }

  onToggleLoop(event) {
    // "this is undefined??" <--- here
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
  }

Voglio aggiornare lo loopActivestato su Attiva / Disattiva, ma l' thisoggetto non è definito nel gestore. Secondo il doc tutorial, thisdovrei fare riferimento al componente. Mi sto perdendo qualcosa?

Risposte:


211

ES6 React.Component non associa automaticamente i metodi a se stesso. Devi vincolarli tu stesso nel costruttore. Come questo:

constructor (props){
  super(props);

  this.state = {
      loopActive: false,
      shuffleActive: false,
    };

  this.onToggleLoop = this.onToggleLoop.bind(this);

}

23
se cambi la tua proprietà onClick () => this.onToggleLoopdopo aver spostato la funzione onToggleLoop nella tua classe di reazione, funzionerà anche.
— Sam,

71
Devi davvero vincolare tutti i metodi di ogni classe di reazione? Non è un po 'folle?
— Alex L

6
@AlexL Ci sono modi per farlo senza legare esplicitamente i metodi. se usi babel è possibile dichiarare tutti i metodi sul componente React come funzioni freccia. Ci sono esempi qui: babeljs.io/blog/2015/06/07/react-on-es6-plus
— Ivan

7
Ma perché thisnon è definito in primo luogo? So che thisin Javascript dipende da come viene chiamata la funzione, ma cosa sta succedendo qui?
— Maverick,

1
ma come posso fare questo è la funzione non è definita fino a dopo il costruttore? Ottengo un "Impossibile leggere la proprietà 'bind' di undefined" se provo a farlo nel costruttore anche se la mia funzione è definita sulla classe :(
— rex

87

Ci sono un paio di modi.

Uno è quello di aggiungere this.onToggleLoop = this.onToggleLoop.bind(this);nel costruttore.

Un altro è le funzioni freccia onToggleLoop = (event) => {...}.

E poi c'è onClick={this.onToggleLoop.bind(this)}.


1
Perché onToogleLoop = () => {} funziona? Ho avuto lo stesso problema e lo rilevo nel mio costruttore ma non ha funzionato ... e ora ho visto il tuo post e ho sostituito il mio metodo con una sintassi della funzione freccia e funziona. Puoi spiegarmelo?
— Guchelkaben,

5
da developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ; Una funzione freccia non crea questo proprio, viene utilizzato questo valore del contesto di esecuzione allegato.
— J. Mark Stevens,

1
Si noti che il collegamento inline in onClickrestituirà una nuova funzione ogni rendering e quindi sembra che sia stato passato un nuovo valore per il prop, facendo casino con shouldComponentUpdatein PureComponents.
— Ninjakannon,

24

Scrivi la tua funzione in questo modo:

onToggleLoop = (event) => {
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
}

Funzioni freccia grassa

l'associazione per la parola chiave è la stessa all'esterno e all'interno della funzione freccia grassa. Ciò è diverso dalle funzioni dichiarate con la funzione, che può associarlo a un altro oggetto al momento dell'invocazione. Mantenere questa associazione è molto conveniente per operazioni come la mappatura: this.items.map (x => this.doSomethingWith (x)).


Se lo faccio ottengo ReferenceError: fields are not currently supported.
— Pavel Komarov,

Funziona se all'interno del costruttore dico this.func = () => {...}, ma lo considero un tipo sciocco e voglio evitarlo se possibile.
— Pavel Komarov,

Così terribile che non puoi usare la normale sintassi della classe in React!
— Kokodoko,

11

Mi sono imbattuto in un legame simile in una funzione di rendering e ho finito per passare il contesto thisnel modo seguente:

{someList.map(function(listItem) {
  // your code
}, this)}

Ho anche usato:

{someList.map((listItem, index) =>
    <div onClick={this.someFunction.bind(this, listItem)} />
)}

Ci sono molte funzioni non necessarie che stai creando lì, ogni volta che viene visualizzato l'elenco ...
— TJ Crowder,

@TJCrowder Sì, è vero che queste funzioni vengono create di nuovo ogni volta che viene chiamato il rendering. È meglio creare le funzioni come metodi di classe e associarle una volta alla classe, ma per i principianti l'associazione manuale al contesto può essere utile
— duhaime

2

Dovresti notare che thisdipende da come viene invocata la funzione, ovvero: quando una funzione viene chiamata come metodo di un oggetto, thisviene impostata sull'oggetto su cui viene chiamato il metodo.

thisè accessibile nel contesto JSX come oggetto componente, quindi è possibile chiamare il metodo desiderato in linea come thismetodo.

Se passi semplicemente il riferimento a funzione / metodo, sembra che la reazione lo invocherà come funzione indipendente.

onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined

onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object

1
Bene, puoi persino usare la prima riga, cioè a onClick={this.onToggleLoop}condizione che nella tua classe di componenti tu abbia definito un campo (proprietà)onToggleLoop = () => /*body using 'this'*/
— gvlax,

1

Se stai usando babel, associ 'questo' usando l'operatore di binding ES7 https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding

export default class SignupPage extends React.Component {
  constructor(props) {
    super(props);
  }

  handleSubmit(e) {
    e.preventDefault(); 

    const data = { 
      email: this.refs.email.value,
    } 
  }

  render() {

    const {errors} = this.props;

    return (
      <div className="view-container registrations new">
        <main>
          <form id="sign_up_form" onSubmit={::this.handleSubmit}>
            <div className="field">
              <input ref="email" id="user_email" type="email" placeholder="Email"  />
            </div>
            <div className="field">
              <input ref="password" id="user_password" type="new-password" placeholder="Password"  />
            </div>
            <button type="submit">Sign up</button>
          </form>
        </main>
      </div>
    )
  }

}

0

Se chiami il tuo metodo creato nei metodi del ciclo di vita come componentDidMount ... allora puoi usare solo la funzione this.onToggleLoop = this.onToogleLoop.bind(this)e fat arrow onToggleLoop = (event) => {...}.

L'approccio normale alla dichiarazione di una funzione nel costruttore non funzionerà perché i metodi del ciclo di vita sono chiamati in precedenza.


0

nel mio caso questa era la soluzione = () => {}

methodName = (params) => {
//your code here with this.something
}

0

Nel mio caso, per un componente apolide che ha ricevuto il riferimento con forwardRef, ho dovuto fare ciò che viene detto qui https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd

Da questo (onClick non ha accesso all'equivalente di 'this')

const Com = forwardRef((props, ref) => {
  return <input ref={ref} onClick={() => {console.log(ref.current} } />
})

A questo (funziona)

const useCombinedRefs = (...refs) => {
  const targetRef = React.useRef()

  useEffect(() => {
    refs.forEach(ref => {
      if (!ref) return

      if (typeof ref === 'function') ref(targetRef.current)
      else ref.current = targetRef.current
    })
  }, [refs])

  return targetRef
}

const Com = forwardRef((props, ref) => {
  const innerRef = useRef()
  const combinedRef = useCombinedRefs(ref, innerRef)

  return <input ref={combinedRef } onClick={() => {console.log(combinedRef .current} } />
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.