Chiama il metodo figlio dal genitore


475

Ho due componenti.

  1. Componente padre
  2. Componente figlio

Stavo cercando di chiamare il metodo del bambino da Parent, ho provato in questo modo ma non sono riuscito a ottenere un risultato

class Parent extends Component {
  render() {
    return (
      <Child>
        <button onClick={Child.getAlert()}>Click</button>
      </Child>
      );
    }
  }

class Child extends Component {
  getAlert() {
    alert('clicked');
  }

  render() {
    return (
      <h1 ref="hello">Hello</h1>
    );
  }
}

C'è un modo per chiamare il metodo del bambino dal genitore?

Nota: i componenti figlio e padre si trovano in due file diversi


Anche se sono in ritardo, sto anche imparando React, quindi vorrei sapere il caso in cui un genitore avrebbe bisogno di chiamare il metodo figlio. Potresti spiegare per favore?
Akshay Raut il

Qualcuno sa come farlo dalle funzioni freccia? stackoverflow.com/questions/60015693/…
Thomas Segato

@AkshayRaut IMO un buon caso d'uso: un modulo per scopi generici con funzioni di reset e di invio, il più tardi dei quali restituisce i valori del modulo.
Julian K,

Risposte:


702

Prima di tutto, lasciatemi esprimere che questo è generalmente non è il modo di fare le cose in Reagire terreno. Di solito, ciò che vuoi fare è trasmettere funzionalità ai bambini negli oggetti di scena e trasmettere notifiche dai bambini negli eventi (o meglio ancora:dispatch .

Ma se devi esporre un metodo imperativo su un componente figlio, puoi usare refs . Ricorda che questo è un portello di fuga e di solito indica che è disponibile un design migliore.

In precedenza, i riferimenti erano supportati solo per i componenti basati su Class. Con l'avvento di React Hooks , non è più così

Uso di hook e componenti funzionali ( >= react@16.8)

const { forwardRef, useRef, useImperativeHandle } = React;

// We need to wrap component in `forwardRef` in order to gain
// access to the ref object that is assigned using the `ref` prop.
// This ref is passed as the second parameter to the function component.
const Child = forwardRef((props, ref) => {

  // The component instance will be extended
  // with whatever you return from the callback passed
  // as the second argument
  useImperativeHandle(ref, () => ({

    getAlert() {
      alert("getAlert from Child");
    }

  }));

  return <h1>Hi</h1>;
});

const Parent = () => {
  // In order to gain access to the child component instance,
  // you need to assign it to a `ref`, so we call `useRef()` to get one
  const childRef = useRef();

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={() => childRef.current.getAlert()}>Click</button>
    </div>
  );
};

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

<div id="root"></div>

La documentazione per useImperativeHandle()è qui :

useImperativeHandlepersonalizza il valore dell'istanza esposto ai componenti principali durante l'utilizzo ref.

Utilizzo dei componenti di classe ( >= react@16.4)

const { Component } = React;

class Parent extends Component {
  constructor(props) {
    super(props);
    this.child = React.createRef();
  }

  onClick = () => {
    this.child.current.getAlert();
  };

  render() {
    return (
      <div>
        <Child ref={this.child} />
        <button onClick={this.onClick}>Click</button>
      </div>
    );
  }
}

class Child extends Component {
  getAlert() {
    alert('getAlert from Child');
  }

  render() {
    return <h1>Hello</h1>;
  }
}

ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

API legacy ( <= react@16.3)

Per scopi storici, ecco lo stile basato sul callback che useresti con le versioni di React prima della 16.3:

const { Component } = React;
const { render } = ReactDOM;

class Parent extends Component {
  render() {
    return (
      <div>
        <Child ref={instance => { this.child = instance; }} />
        <button onClick={() => { this.child.getAlert(); }}>Click</button>
      </div>
    );
  }
}

class Child extends Component {
  getAlert() {
    alert('clicked');
  }

  render() {
    return (
      <h1>Hello</h1>
    );
  }
}


render(
  <Parent />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app"></div>


23
Sono stanco, ma finisco con questo errore "_this2.refs.child.getAlert non è una funzione"
N8FURY

22
Questo perché connectrestituisce un componente di ordine superiore che avvolge l'istanza originale. Dovrai prima chiamare getWrappedInstance()il componente collegato per ottenere il componente originale. Quindi puoi chiamare i metodi di istanza su questo.
rossipedia,

16
Questo non è davvero un buon modello. Per non parlare dei riferimenti alle stringhe sono disapprovati. È meglio passare oggetti di scena nel componente figlio e quindi fare clic su un pulsante nel genitore per modificare lo stato del genitore e passare un elemento di stato nel figlio che attiverà quello del figlio componentWillReceivePropse utilizzarlo come trigger.
ffxsam,

8
No, di solito non è il modello migliore, è più un portello di fuga quando ne hai bisogno e dovrebbe essere usato solo in caso di emergenza. Inoltre, questa risposta è stata scritta quando i riferimenti alle stringhe erano ancora in giro, e hai ragione che non sono il modo "corretto" di fare le cose in questi giorni.
rossipedia

35
Se la migliore pratica è creare un labirinto di logica per fare qualcosa di semplice come chiamare il metodo di un componente figlio, allora non sono d'accordo con la migliore pratica.
aaaaaa,

150

Puoi usare un altro modello qui:

class Parent extends Component {
 render() {
  return (
    <div>
      <Child setClick={click => this.clickChild = click}/>
      <button onClick={() => this.clickChild()}>Click</button>
    </div>
  );
 }
}

class Child extends Component {
 constructor(props) {
    super(props);
    this.getAlert = this.getAlert.bind(this);
 }
 componentDidMount() {
    this.props.setClick(this.getAlert);
 }
 getAlert() {
    alert('clicked');
 }
 render() {
  return (
    <h1 ref="hello">Hello</h1>
  );
 }
}

Quello che fa è impostare il clickChildmetodo del genitore quando il figlio è montato. In questo modo, quando si fa clic sul pulsante genitore, verrà chiamato il clickChildquale chiama il bambino getAlert.

Questo funziona anche se il tuo bambino è avvolto con, connect()quindi non hai bisogno getWrappedInstance()dell'hack.

Nota che non puoi usare onClick={this.clickChild}in parent perché quando parent è reso child non è montato, quindi this.clickChildnon è ancora assegnato. L'uso onClick={() => this.clickChild()}va bene perché quando si fa clic sul pulsante this.clickChilddovrebbe già essere assegnato.


5
Capisco _this2.clickChild is not a functionperché?
Tatsu,

1
non importa che questo abbia funzionato per me: github.com/kriasoft/react-starter-kit/issues/…
tatsu,

5
né ha funzionato. solo questa risposta ha funzionato: github.com/kriasoft/react-starter-kit/issues/…
tatsu

2
Questa è una tecnica interessante. È abbastanza pulito e non sembra infrangere alcuna regola. Ma penso che la tua risposta sarebbe più completa (e soddisfare le aspettative) se aggiungessi un legame. La risposta mi è piaciuta così tanto che l'ho pubblicata su questo problema relativo a Github .
joeytwiddle,

8
Questa dovrebbe essere la risposta accettata
Jesus Gomez,

28

https://facebook.github.io/react/tips/expose-component-functions.html per ulteriori risposte, fare riferimento qui Chiama i metodi sui componenti dei bambini React

Esaminando i riferimenti del componente "reason", si rompe l'incapsulamento e si rende impossibile il refactoring di quel componente senza esaminare attentamente tutti i luoghi in cui viene utilizzato. Per questo motivo, consigliamo vivamente di trattare i ref come privati ​​di un componente, in modo molto simile allo stato.

In generale, i dati dovrebbero essere passati all'albero tramite oggetti di scena. Ci sono alcune eccezioni a questo (come la chiamata a .focus () o l'attivazione di un'animazione di una volta che in realtà non "cambia" lo stato) ma ogni volta che si espone un metodo chiamato "set", gli oggetti di scena sono di solito una scelta migliore. Cerca di fare in modo che il componente di input interno si preoccupi delle sue dimensioni e del suo aspetto in modo che nessuno dei suoi antenati lo faccia.


5
Ecco la fonte di questa risposta: discuss.reactjs.org/t/… . Nessun problema nel citare gli altri, ma almeno aggiungere qualche riferimento.
Jodo,

1
In che modo questa incapsulamento interrompe più dei puntelli?
Timmmm

17

Metodo alternativo con useEffect:

Genitore:

const [refresh, doRefresh] = useState(0);
<Button onClick={()=>doRefresh(refresh+1)} />
<Children refresh={refresh} />

Bambini:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);

2
Ciò dovrebbe dare più voti
Ali Al Amine,

Ps. Se il tuo desiderio è solo di rendere nuovamente il modulo (ad esempio, per ripristinare i campi di input), non è nemmeno necessario includere useEffect, puoi semplicemente fare in modo che l'elica venga inviata nella modifica del componente
Matt Fletcher

8

Possiamo usare refs in un altro modo come-

Creeremo un elemento Parent, renderà un <Child/>componente. Come puoi vedere, il componente che verrà renderizzato, devi aggiungere l' attributo ref e fornirne un nome.
Quindi, la triggerChildAlertfunzione, situata nella classe genitrice, accederà alla proprietà refs di questo contesto (quando la triggerChildAlertfunzione viene attivata accederà al riferimento figlio e avrà tutte le funzioni dell'elemento figlio).

class Parent extends React.Component {
    triggerChildAlert(){
        this.refs.child.callChildMethod();
        // to get child parent returned  value-
        // this.value = this.refs.child.callChildMethod();
        // alert('Returned value- '+this.value);
    }

    render() {
        return (
            <div>
                {/* Note that you need to give a value to the ref parameter, in this case child*/}
                <Child ref="child" />
                <button onClick={this.triggerChildAlert}>Click</button>
            </div>
        );
    }
}  

Ora, il componente figlio, come teoricamente progettato in precedenza, sarà simile a:

class Child extends React.Component {
    callChildMethod() {
        alert('Hello World');
        // to return some value
        // return this.state.someValue;
    }

    render() {
        return (
            <h1>Hello</h1>
        );
    }
}

Ecco il codice sorgente:
Hope ti aiuterà!



4

Se lo stai facendo semplicemente perché vuoi che il Bambino fornisca un tratto riutilizzabile ai suoi genitori, allora potresti considerare di farlo usando render-props .

Quella tecnica in realtà capovolge la struttura. L' Childora avvolge il genitore, quindi l'ho rinominato in AlertTraitbasso. Ho mantenuto il nome Parentper continuità, anche se non è davvero un genitore ora.

// Use it like this:

  <AlertTrait renderComponent={Parent}/>


class AlertTrait extends Component {
  // You may need to bind this function, if it is stateful
  doAlert() {
    alert('clicked');
  }
  render() {
    return this.props.renderComponent(this.doAlert);
  }
}

class Parent extends Component {
  render() {
    return (
      <button onClick={this.props.doAlert}>Click</button>
    );
  }
}

In questo caso, AlertTrait fornisce uno o più tratti che trasmette come oggetti di scena a qualunque componente gli sia stato dato renderComponent.

Il genitore riceve doAlertcome oggetto di scena e può chiamarlo quando necessario.

(Per chiarezza, ho chiamato il sostegno renderComponentnell'esempio sopra. Ma nei documenti React collegati sopra, lo chiamano semplicemente render.)

Il componente Trait può eseguire il rendering di elementi che circondano il genitore, nella sua funzione di rendering, ma non rende nulla all'interno del genitore. In realtà potrebbe rendere le cose all'interno del genitore, se passasse un altro oggetto di scena (ad esrenderChild ) Al genitore, che il genitore potrebbe quindi utilizzare durante il suo metodo di rendering.

Questo è in qualche modo diverso da ciò che l'OP ha richiesto, ma alcune persone potrebbero finire qui (come abbiamo fatto noi) perché volevano creare un carattere riutilizzabile e pensavano che un componente figlio fosse un buon modo per farlo.


C'è un utile elenco di modelli per la creazione di tratti riutilizzabili qui: reazionejs.org/blog/2016/07/13/…
joeytwiddle

Che cosa succede se hai N cronometri e un pulsante per riavviarli tutti. Come sono utili gli oggetti di rendering qui?
vsync,

@vsync Non sono sicuro che questo metodo possa aiutarti per il tuo compito. Ma la risposta di brickingup potrebbe aiutare. Tieni presente che sono impostati, this.clickChild = clickma i tuoi cronometri multipli passerebbero a più funzioni, quindi dovrai memorizzarli tutti:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle

1

Puoi farlo facilmente in questo modo

Steps-

  1. Crea una variabile booleana nello stato nella classe genitore. Aggiorna questo quando vuoi chiamare una funzione.
  2. Crea una variabile prop e assegna la variabile booleana.
  3. Dal componente figlio accedi a quella variabile usando i puntelli ed esegui il metodo desiderato avendo una condizione if.

    class Child extends Component {
       Method=()=>{
       --Your method body--
       }
       render() {
         return (
        //check whether the variable has been updated or not
          if(this.props.updateMethod){
            this.Method();
          }
         )
       }
    }
    
    class Parent extends Component {
    
    constructor(){
      this.state={
       callMethod:false
      }
    
    }
    render() {
       return (
    
         //update state according to your requirement
         this.setState({
            callMethod:true
         }}
         <Child updateMethod={this.state.callMethod}></Child>
        );
       }
    }

Potresti voler sandbox questo. Sembra che finirai con un ciclo infinito perché il metodo figlio verrà eseguito continuamente perché lo stato padre è impostato su true.
Isaac Pak,

@IsaacPak Sì, ecco perché ho lasciato un commento lì, dicendo che devi aggiornare lo stato in base alle tue esigenze. Quindi non funzionerà come un ciclo infinito.
Kusal Kithmal,

1

Sto usando useEffecthook per superare il mal di testa di fare tutto questo, quindi ora passo una variabile al bambino in questo modo:

<ParentComponent>
 <ChildComponent arbitrary={value} />
</ParentComponent>
useEffect(() => callTheFunctionToBeCalled(value) , [value]);

1

Non ero soddisfatto di nessuna delle soluzioni presentate qui. Esiste in realtà una soluzione molto semplice che può essere fatta usando Javascript puro senza fare affidamento su alcune funzionalità di React diverse dall'oggetto di base props - e ti dà il vantaggio di comunicare in entrambe le direzioni (genitore -> figlio, figlio -> genitore). È necessario passare un oggetto dal componente padre al componente figlio. Questo oggetto è quello che io chiamo "riferimento bidirezionale" o abbreviazione di biRef. Fondamentalmente, l'oggetto contiene un riferimento ai metodi nel genitore che il genitore vuole esporre. E il componente figlio associa metodi all'oggetto che il genitore può chiamare. Qualcosa come questo:

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      someParentFunction: someParentFunction
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef.someChildFunction = someChildFunction;

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

L'altro vantaggio di questa soluzione è che puoi aggiungere molte più funzioni nel genitore e nel figlio mentre le passi dal genitore al figlio usando una sola proprietà.

Un miglioramento rispetto al codice sopra è quello di non aggiungere le funzioni padre e figlio direttamente all'oggetto biRef ma piuttosto ai membri secondari. Le funzioni padre devono essere aggiunte a un membro chiamato "padre" mentre le funzioni figlio devono essere aggiunte a un membro chiamato "figlio".

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.child.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      parent: {
          someParentFunction: someParentFunction
      }
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.parent.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef {
       child: {
            someChildFunction: someChildFunction
       }
   }

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

Posizionando le funzioni genitore e figlio in membri separati dell'oggetto biRef, avrete una netta separazione tra i due e vedrete facilmente quali appartengono al genitore o al figlio. Aiuta anche a impedire che un componente figlio sovrascriva accidentalmente una funzione genitore se la stessa funzione appare in entrambi.

Un'ultima cosa è che, se si nota, il componente genitore crea l'oggetto biRef con var mentre il componente figlio accede ad esso tramite l'oggetto props. Potrebbe essere allettante non definire l'oggetto biRef nel genitore e accedervi dal suo genitore attraverso il proprio parametro props (che potrebbe essere il caso in una gerarchia di elementi dell'interfaccia utente). Questo è rischioso perché il bambino può pensare che una funzione che sta chiamando sul genitore appartenga al genitore quando potrebbe effettivamente appartenere a un nonno. Non c'è niente di sbagliato in questo, purché tu ne sia consapevole. A meno che tu non abbia una ragione per supportare una gerarchia al di là di una relazione genitore / figlio, è meglio creare il biRef nel tuo componente genitore.



0

Penso che il modo più semplice per chiamare i metodi sia impostando una richiesta sul componente figlio. Quindi non appena il figlio gestisce la richiesta, chiama un metodo di richiamata per reimpostare la richiesta.

Il meccanismo di ripristino è necessario per poter inviare la stessa richiesta più volte una dopo l'altra.

Nel componente principale

Nel metodo di rendering del genitore:

const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);

Il genitore ha bisogno di 2 metodi, per comunicare con il figlio in 2 direzioni.

sendRequest() {
  const request = { param: "value" };
  this.setState({ request });
}

resetRequest() {
  const request = null;
  this.setState({ request });
}

Nel componente figlio

Il bambino aggiorna il suo stato interno, copiando la richiesta dagli oggetti di scena.

constructor(props) {
  super(props);
  const { request } = props;
  this.state = { request };
}

static getDerivedStateFromProps(props, state) {
  const { request } = props;
  if (request !== state.request ) return { request };
  return null;
}

Quindi, infine, gestisce la richiesta e invia il ripristino al genitore:

componentDidMount() {
  const { request } = this.state;
  // todo handle request.

  const { onRequestHandled } = this.props;
  if (onRequestHandled != null) onRequestHandled();
}

0

Un altro modo per attivare una funzione figlio dal genitore consiste nell'utilizzare la componentDidUpdatefunzione in Componente figlio. Passo un sostegno triggerChildFuncda genitore a figlio, che inizialmente lo è null. Il valore cambia in una funzione quando si fa clic sul pulsante e Child si accorge che cambia componentDidUpdatee chiama la propria funzione interna.

Poiché il prop si triggerChildFunctrasforma in una funzione, otteniamo anche una richiamata al genitore. Se il genitore non ha bisogno di sapere quando viene chiamata la funzione, triggerChildFuncad esempio il valore potrebbe cambiare da nulla true.

const { Component } = React;
const { render } = ReactDOM;

class Parent extends Component {
  state = {
    triggerFunc: null
  }

  render() {
    return (
      <div>
        <Child triggerChildFunc={this.state.triggerFunc} />
        <button onClick={() => {
          this.setState({ triggerFunc: () => alert('Callback in parent')})
        }}>Click
        </button>
      </div>
    );
  }
}

class Child extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) {
      this.onParentTrigger();
    }
  }

  onParentTrigger() {
    alert('parent triggered me');

    // Let's call the passed variable from parent if it's a function
    if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') {
      this.props.triggerChildFunc();
    }
  }

  render() {
    return (
      <h1>Hello</h1>
    );
  }
}


render(
  <Parent />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>

CodePen: https://codepen.io/calsal/pen/NWPxbJv?editors=1010


0

Ecco la mia demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Sto usando useEffectper chiamare i metodi del componente children. Ho provato con Proxy and Setter_Getterma sor useEffectsembra essere il modo più conveniente per chiamare un metodo figlio dal genitore. Per usarlo Proxy and Setter_Gettersembra che ci sia un po 'di sottigliezza da superare per prima, perché l'elemento reso in primo luogo è un oggetto come l'elemento attraverso la ref.current return => <div/>specificità. Per quanto riguarda useEffect, puoi anche sfruttare questo approccio per impostare lo stato del genitore a seconda di cosa vuoi fare con i bambini.

Nel link della demo che ho fornito, troverai il mio codice completo di ReactJS con le mie bozze all'interno in modo da poter apprezzare il flusso di lavoro della mia soluzione.

Qui ti sto fornendo lo snippet del mio ReactJS solo con il relativo codice. :

import React, {
  Component,
  createRef,
  forwardRef,
  useState,
  useEffect
} from "react"; 

{...}

// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
  // I am fetching the parent's method here
  // that allows me to connect the parent and the child's components
  let { validateChildren } = props;
  // I am initializing the state of the children
  // good if we can even leverage on the functional children's state
  let initialState = {
    one: "hello world",
    two: () => {
      console.log("I am accessing child method from parent :].");
      return "child method achieve";
    }
  };
  // useState initialization
  const [componentState, setComponentState] = useState(initialState);
  // useEffect will allow me to communicate with the parent
  // through a lifecycle data flow
  useEffect(() => {
    ref.current = { componentState };
    validateChildren(ref.current.componentState.two);
  });

{...}

});

{...}

// Parent component
class App extends Component {
  // initialize the ref inside the constructor element
  constructor(props) {
    super(props);
    this.childRef = createRef();
  }

  // I am implementing a parent's method
  // in child useEffect's method
  validateChildren = childrenMethod => {
    // access children method from parent
    childrenMethod();
    // or signaling children is ready
    console.log("children active");
  };

{...}
render(){
       return (
          {
            // I am referencing the children
            // also I am implementing the parent logic connector's function
            // in the child, here => this.validateChildren's function
          }
          <Child ref={this.childRef} validateChildren={this.validateChildren} />
        </div>
       )
}

0

Siamo contenti di un gancio personalizzato che chiamiamo useCounterKey. Imposta solo un counterKey o una chiave che conta da zero. La funzione che restituisce reimposta il tasto (ovvero l'incremento). (Credo che questo sia il modo più idiomatico in React per resettare un componente - basta premere il tasto.)

Tuttavia, questo hook funziona anche in qualsiasi situazione in cui si desidera inviare un messaggio singolo al client per fare qualcosa. Ad esempio, lo usiamo per focalizzare un controllo nel bambino su un determinato evento genitore - si focalizza automaticamente ogni volta che la chiave viene aggiornata. (Se sono necessari più oggetti di scena, potrebbero essere impostati prima di ripristinare la chiave in modo che siano disponibili quando si verifica l'evento.)

Questo metodo ha un po 'di una curva di apprendimento in b / c non è così semplice come un tipico gestore di eventi, ma sembra il modo più idiomatico di gestirlo in React che abbiamo trovato (poiché i tasti funzionano già in questo modo). Sicuramente aperto al feedback su questo metodo, ma funziona bene!

// Main helper hook:
export function useCounterKey() {
  const [key, setKey] = useState(0);
  return [key, () => setKey(prev => prev + 1)] as const;
}

Esempi di utilizzo:

// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
  const [inputLineCounterKey, resetInputLine] = useCounterKey();

  return <>
    <InputLine key={inputLineCounterKey} />
    <button onClick={() => resetInputLine()} />
  <>;
}

// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
  const [amountFocusCounterKey, focusAmountInput] = useCounterKey();

  // ... call focusAmountInput in some hook or event handler as needed

  return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}

function WorkoutAmountInput(props) {
  useEffect(() => {
    if (counterKey > 0) {
      // Don't focus initially
      focusAmount();
    }
  }, [counterKey]);

  // ...
}

(Ringraziamo Kent Dodds per il concetto di counterKey .)


-1

Ecco un bug? a cui prestare attenzione: concordo con la soluzione di rossipedia usando forwardRef, useRef, useImperativeHandle

C'è qualche disinformazione online che dice che i ref possono essere creati solo dai componenti di React Class, ma puoi effettivamente usare i Componenti di funzione se usi i suddetti hook sopra. Nota: gli hook hanno funzionato per me solo dopo aver modificato il file per non utilizzarlo conRouter () durante l'esportazione del componente. Cioè un cambiamento da

export default withRouter(TableConfig);

essere invece

export default TableConfig;

Con il senno di poi withRouter () non è comunque necessario per un componente del genere, ma di solito non danneggia nulla che lo abbia. Il mio caso d'uso è che ho creato un componente per creare una tabella per gestire la visualizzazione e la modifica dei valori di configurazione, e volevo essere in grado di dire a questo componente figlio di reimpostare i suoi valori di stato ogni volta che veniva premuto il pulsante Reimposta del modulo padre. UseRef () non otterrebbe correttamente ref o ref.current (continuando a diventare null) fino a quando non rimossi conRouter () dal file contenente il mio componente figlio TableConfig

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.