Appel de la méthode enfant depuis le parent


475

J'ai deux composantes.

  1. Composant parent
  2. Composant enfant

J'essayais d'appeler la méthode de l'enfant de Parent, j'ai essayé de cette façon mais je n'ai pas pu obtenir de résultat

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

Existe-t-il un moyen d'appeler la méthode de l'enfant du parent?

Remarque: les composants enfant et parent se trouvent dans deux fichiers différents


Bien que je sois très en retard à cela, j'apprends également React, donc j'aimerais connaître le cas dans lequel un parent devrait appeler la méthode enfant. Pourriez-vous s'il vous plaît expliquer?
Akshay Raut

Tout le monde sait comment cela à partir des fonctions fléchées? stackoverflow.com/questions/60015693/…
Thomas Segato

@AkshayRaut IMO un bon cas d'utilisation: un formulaire à usage général avec des fonctions de réinitialisation et de soumission, dont la dernière renvoie les valeurs du formulaire.
Julian K

Réponses:


702

Tout d'abord, permettez-moi d'exprimer que ce n'est généralement pas la façon de procéder dans React Land. Habituellement, ce que vous voulez faire, c'est transmettre la fonctionnalité aux enfants dans les accessoires et transmettre les notifications des enfants dans les événements (ou mieux encore:) dispatch.

Mais si vous devez exposer une méthode impérative sur un composant enfant, vous pouvez utiliser des références . N'oubliez pas qu'il s'agit d'une trappe d'évacuation et indique généralement qu'un meilleur design est disponible.

Auparavant, les références n'étaient prises en charge que pour les composants basés sur les classes. Avec l'avènement de React Hooks , ce n'est plus le cas

Utilisation de crochets et de composants de fonction ( >= 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 documentation pour useImperativeHandle()est ici :

useImperativeHandlepersonnalise la valeur d'instance exposée aux composants parents lors de l'utilisation ref.

Utilisation des composants de 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 héritée ( <= react@16.3)

À des fins historiques, voici le style basé sur le rappel que vous utiliseriez avec les versions React antérieures à 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
Je suis fatigué, mais je me retrouve avec cette erreur "_this2.refs.child.getAlert n'est pas une fonction"
N8FURY

22
En effet, connectrenvoie un composant d'ordre supérieur qui enveloppe votre instance d'origine. Vous devrez d'abord appeler getWrappedInstance()le composant connecté pour obtenir votre composant d'origine. Ensuite, vous pouvez appeler des méthodes d'instance à ce sujet.
rossipedia

16
Ce n'est pas vraiment un bon schéma. Sans parler des références de chaîne sont mal vues. Il est préférable de passer des accessoires dans le composant enfant, puis de cliquer sur le bouton du parent pour modifier l'état du parent, et de passer un élément d'état à l'enfant qui déclenchera l'enfant componentWillReceiveProps, et l'utiliser comme déclencheur.
ffxsam

8
Non, ce n'est généralement pas le meilleur modèle, c'est plutôt une trappe d'évacuation lorsque vous en avez besoin et ne doit être utilisé qu'en cas d'urgence. En outre, cette réponse a été écrite lorsque les références de chaîne étaient toujours présentes, et vous avez raison de dire qu'elles ne sont pas la "bonne" façon de faire les choses de nos jours.
rossipedia

35
Si la meilleure pratique consiste à créer un labyrinthe de logique pour faire quelque chose d'aussi simple que d'appeler la méthode d'un composant enfant - alors je ne suis pas d'accord avec la meilleure pratique.
aaaaaa

150

Vous pouvez utiliser un autre modèle ici:

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

Ce qu'il fait, c'est de définir la clickChildméthode du parent lorsque l'enfant est monté. De cette façon, lorsque vous cliquez sur le bouton parent, il appelle celui clickChildqui appelle l'enfant getAlert.

Cela fonctionne également si votre enfant est enveloppé de connect()sorte que vous n'avez pas besoin du getWrappedInstance()hack.

Notez que vous ne pouvez pas l'utiliser onClick={this.clickChild}dans parent car lorsque le parent est rendu, l'enfant n'est pas monté et this.clickChildn'est donc pas encore attribué. L'utilisation onClick={() => this.clickChild()}est très bien car lorsque vous cliquez sur le bouton this.clickChilddoit déjà être attribué.


5
Je comprends _this2.clickChild is not a functionpourquoi?
tatsu

1
peu importe que cela fonctionne pour moi: github.com/kriasoft/react-starter-kit/issues/…
tatsu

5
ni travaillé. seule cette réponse a fonctionné: github.com/kriasoft/react-starter-kit/issues/…
tatsu

2
C'est une technique intéressante. Il est assez propre et ne semble enfreindre aucune règle. Mais je pense que votre réponse serait plus complète (et répondrait aux attentes) si vous ajoutiez une liaison. J'ai tellement aimé la réponse que je l'ai postée sur ce problème lié à Github .
joeytwiddle

8
Cela devrait être la réponse acceptée
Jesus Gomez

28

https://facebook.github.io/react/tips/expose-component-functions.html pour plus de réponses référez-vous ici Méthodes d'appel sur les composants enfants React

En examinant les références du composant "raison", vous cassez l'encapsulation et vous ne pouvez pas refactoriser ce composant sans examiner attentivement tous les endroits où il est utilisé. Pour cette raison, nous recommandons fortement de traiter les références comme privées à un composant, un peu comme l'état.

En général, les données doivent être transmises dans l'arbre via des accessoires. Il y a quelques exceptions à cela (comme appeler .focus () ou déclencher une animation ponctuelle qui ne "change" pas vraiment l'état) mais chaque fois que vous exposez une méthode appelée "set", les accessoires sont généralement un meilleur choix. Essayez de faire en sorte que le composant d'entrée interne s'inquiète de sa taille et de son apparence afin qu'aucun de ses ancêtres ne le fasse.


5
Voici la source de cette réponse: discuter.reactjs.org/t/… . Pas de problème pour en citer d'autres, mais au moins mettre une référence.
Jodo

1
Comment cette rupture encapsule-t-elle plus que les accessoires?
Timmmm

17

Méthode alternative avec useEffect:

Parent:

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

Enfants:

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

2
Cela devrait obtenir plus de votes
Ali Al Amine

Ps. Si vous souhaitez simplement restituer le formulaire (par exemple, pour réinitialiser les champs de saisie), vous n'avez même pas besoin d'inclure useEffect, vous pouvez simplement faire en sorte que l'hélice soit envoyée dans le changement de composant
Matt Fletcher

8

Nous pouvons utiliser les références d'une autre manière

Nous allons créer un élément Parent, il rendra un <Child/>composant. Comme vous pouvez le voir, le composant qui sera rendu, vous devez ajouter l' attribut ref et lui donner un nom.
Ensuite, la triggerChildAlertfonction, située dans la classe parente, accédera à la propriété refs de ce contexte (lorsque la triggerChildAlertfonction sera déclenchée, elle accédera à la référence enfant et aura toutes les fonctions de l'élément enfant).

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

Maintenant, le composant enfant, tel que théoriquement conçu précédemment, ressemblera à:

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

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

Voici le code source -
Hope vous aidera!


1
Les références de chaîne sont obsolètes. reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy

4

Si vous faites cela simplement parce que vous voulez que l'enfant fournisse un trait réutilisable à ses parents, alors vous pourriez envisager de le faire en utilisant des accessoires de rendu à la place.

Cette technique bouleverse la structure. L' Childenveloppe maintenant le parent, donc je l'ai renommé ci- AlertTraitdessous. J'ai gardé le nom Parentde continuité, même si ce n'est plus vraiment un parent maintenant.

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

Dans ce cas, le AlertTrait fournit un ou plusieurs traits qu'il transmet comme accessoires à n'importe quel composant qui lui a été donné dans son renderComponentaccessoire.

Le parent reçoit doAlertcomme accessoire et peut l'appeler en cas de besoin.

(Pour plus de clarté, j'ai appelé l'hélice renderComponentdans l'exemple ci-dessus. Mais dans les documents React liés ci-dessus, ils l'appellent juste render.)

Le composant Trait peut restituer des éléments entourant le parent, dans sa fonction de rendu, mais il ne rend rien à l'intérieur du parent. En fait, il pourrait rendre des choses à l'intérieur du parent, s'il passait un autre accessoire (par exemple renderChild) au parent, que le parent pourrait ensuite utiliser lors de sa méthode de rendu.

C'est quelque peu différent de ce que le PO a demandé, mais certaines personnes pourraient se retrouver ici (comme nous l'avons fait) parce qu'elles voulaient créer un trait réutilisable et pensaient qu'un composant enfant était un bon moyen de le faire.


Il y a une liste pratique de modèles pour créer des traits réutilisables ici: reactjs.org/blog/2016/07/13/…
joeytwiddle

Et si vous avez N chronomètres et un bouton pour tous les redémarrer. Comment rendre les accessoires pratiques ici?
vsync

@vsync Je ne suis pas sûr que cette méthode puisse vous aider dans votre tâche. Mais la réponse de brickingup pourrait aider. Notez qu'ils sont définis, this.clickChild = clickmais vos chronomètres multiples passeraient plusieurs fonctions, vous devriez donc tous les stocker:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle

1

Vous pouvez y parvenir facilement de cette manière

Pas-

  1. Créez une variable booléenne dans l'état de la classe parent. Mettez-le à jour lorsque vous souhaitez appeler une fonction.
  2. Créez une variable prop et affectez la variable booléenne.
  3. À partir du composant enfant, accédez à cette variable à l'aide d'accessoires et exécutez la méthode souhaitée en ayant une condition 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>
        );
       }
    }

Vous voudrez peut-être mettre cela en sandbox. Il semble que vous allez vous retrouver avec une boucle infinie car la méthode enfant s'exécutera continuellement car l'état parent est défini sur true.
Isaac Pak

@IsaacPak Oui, c'est pourquoi j'ai laissé un commentaire là-bas, disant que vous devez mettre à jour l'état en fonction de vos besoins. Ensuite, il ne fonctionnera pas comme une boucle infinie.
Kusal Kithmal

1

J'utilise le useEffectcrochet pour surmonter le mal de tête de faire tout cela alors maintenant je transmets une variable à l'enfant comme ceci:

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

1

Je n'ai été satisfait d'aucune des solutions présentées ici. Il existe en fait une solution très simple qui peut être faite en utilisant du Javascript pur sans compter sur une fonctionnalité React autre que l'objet d'accessoires de base - et cela vous donne l'avantage de communiquer dans les deux sens (parent -> enfant, enfant -> parent). Vous devez passer un objet du composant parent au composant enfant. Cet objet est ce que j'appelle une "référence bidirectionnelle" ou biRef pour faire court. Fondamentalement, l'objet contient une référence aux méthodes du parent que le parent souhaite exposer. Et le composant enfant attache des méthodes à l'objet que le parent peut appeler. Quelque chose comme ça:

// 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'autre avantage de cette solution est que vous pouvez ajouter beaucoup plus de fonctions dans le parent et l'enfant tout en les passant du parent à l'enfant en utilisant une seule propriété.

Une amélioration par rapport au code ci-dessus consiste à ne pas ajouter les fonctions parent et enfant directement à l'objet biRef mais plutôt aux sous-membres. Les fonctions parents doivent être ajoutées à un membre appelé "parent" tandis que les fonctions enfants doivent être ajoutées à un membre appelé "enfant".

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

En plaçant les fonctions parent et enfant dans des membres distincts de l'objet biRef, vous aurez une séparation nette entre les deux et vous verrez facilement celles qui appartiennent au parent ou à l'enfant. Cela aide également à empêcher un composant enfant d'écraser accidentellement une fonction parent si la même fonction apparaît dans les deux.

Une dernière chose est que si vous remarquez, le composant parent crée l'objet biRef avec var tandis que le composant enfant y accède via l'objet props. Il peut être tentant de ne pas définir l'objet biRef dans le parent et d'y accéder depuis son parent via son propre paramètre d'accessoires (ce qui pourrait être le cas dans une hiérarchie d'éléments d'interface utilisateur). Cela est risqué car l'enfant peut penser qu'une fonction qu'il appelle sur le parent appartient au parent alors qu'elle pourrait en fait appartenir à un grand-parent. Il n'y a rien de mal à cela tant que vous en êtes conscient. Sauf si vous avez une raison de prendre en charge une hiérarchie au-delà d'une relation parent / enfant, il est préférable de créer le biRef dans votre composant parent.



0

Je pense que la façon la plus simple d'appeler des méthodes consiste à définir une requête sur le composant enfant. Ensuite, dès que l'enfant gère la demande, il appelle une méthode de rappel pour réinitialiser la demande.

Le mécanisme de réinitialisation est nécessaire pour pouvoir envoyer la même demande plusieurs fois de suite.

Dans le composant parent

Dans la méthode de rendu du parent:

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

Le parent a besoin de 2 méthodes pour communiquer avec son enfant dans 2 directions.

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

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

Dans le composant enfant

L'enfant met à jour son état interne, copiant la demande des accessoires.

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

Enfin, il gère la demande et envoie la réinitialisation au parent:

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

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

0

Une autre façon de déclencher une fonction enfant à partir du parent consiste à utiliser la componentDidUpdatefonction dans le composant enfant. Je passe un accessoire triggerChildFuncde Parent à Enfant, ce qui est initialement le cas null. La valeur se transforme en fonction lorsque l'utilisateur clique sur le bouton et que l'enfant remarque qu'il change componentDidUpdateet appelle sa propre fonction interne.

Étant donné que prop triggerChildFuncchange une fonction, nous obtenons également un rappel au parent. Si Parent n'a pas besoin de savoir quand la fonction est appelée, la valeur triggerChildFuncpeut par exemple changer de nullà à la trueplace.

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

Voici ma démo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

J'utilise useEffectpour appeler les méthodes du composant enfants. J'ai essayé avec, Proxy and Setter_Gettermais de loin, cela useEffectsemble être le moyen le plus pratique d'appeler une méthode enfant à partir du parent. Pour l'utiliser, Proxy and Setter_Getteril semble qu'il y ait une subtilité à surmonter d'abord, car l'élément rendu en premier est un élément objectLike à travers la ref.current return => <div/>spécificité de. Concernant useEffect, vous pouvez également tirer parti de cette approche pour définir l'état du parent en fonction de ce que vous voulez faire avec les enfants.

Dans le lien de la démo que j'ai fourni, vous trouverez mon code ReactJS complet avec mes ébauches à l'intérieur afin que vous puissiez apprécier le flux de travail de ma solution.

Ici, je vous fournis mon extrait de ReactJS avec le code correspondant uniquement. :

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

Nous sommes satisfaits d'un crochet personnalisé que nous appelons useCounterKey. Il configure simplement une counterKey, ou une clé qui compte à partir de zéro. La fonction qu'elle renvoie réinitialise la clé (c'est-à-dire incrémente). (Je pense que c'est la façon la plus idiomatique dans React de réinitialiser un composant - il suffit de bousculer la clé.)

Cependant, ce hook fonctionne également dans toutes les situations où vous souhaitez envoyer un message unique au client pour faire quelque chose. Par exemple, nous l'utilisons pour concentrer un contrôle chez l'enfant sur un certain événement parent - il se concentre automatiquement à chaque fois que la clé est mise à jour. (Si plus d'accessoires sont nécessaires, ils peuvent être définis avant de réinitialiser la clé afin qu'ils soient disponibles lorsque l'événement se produit.)

Cette méthode a un peu une courbe d'apprentissage b / c ce n'est pas aussi simple qu'un gestionnaire d'événements typique, mais cela semble la façon la plus idiomatique de gérer cela dans React que nous avons trouvé (puisque les clés fonctionnent déjà de cette façon). Def ouvert aux commentaires sur cette méthode, mais cela fonctionne bien!

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

Exemples d'utilisation:

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

  // ...
}

( Nous remercions Kent Dodds pour le concept counterKey .)


-1

Voici un bug? à surveiller: je suis d'accord avec la solution de rossipedia utilisant forwardRef, useRef, useImperativeHandle

Il y a des informations erronées en ligne qui disent que les références ne peuvent être créées qu'à partir de composants React Class, mais vous pouvez en effet utiliser les composants de fonction si vous utilisez les crochets susmentionnés. Une note, les crochets n'ont fonctionné pour moi qu'après avoir modifié le fichier pour ne pas utiliser withRouter () lors de l'exportation du composant. C'est à dire un changement de

export default withRouter(TableConfig);

être à la place

export default TableConfig;

Avec le recul, withRouter () n'est de toute façon pas nécessaire pour un tel composant, mais généralement cela ne fait rien de mal à l'avoir. Mon cas d'utilisation est que j'ai créé un composant pour créer une table pour gérer l'affichage et la modification des valeurs de configuration, et je voulais pouvoir dire à ce composant enfant de réinitialiser ses valeurs d'état chaque fois que le bouton de réinitialisation du formulaire parent était enfoncé. UseRef () n'obtiendrait pas correctement le ref ou ref.current (continuait à devenir nul) jusqu'à ce que je supprime withRouter () du fichier contenant mon composant enfant TableConfig

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.