Llamar al método hijo desde el padre

475

Tengo dos componentes

  1. Componente principal
  2. Componente hijo

Estaba tratando de llamar al método del niño desde Parent, lo intenté de esta manera pero no pude obtener un resultado

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

¿Hay alguna forma de llamar al método del niño desde el padre?

Nota: Los componentes secundarios y principales están en dos archivos diferentes.

N8FURY
fuente
Aunque llego muy tarde a esto, también estoy aprendiendo React, así que me gustaría saber sobre el caso en que un padre necesitaría llamar al método child. ¿Podría por favor explicar?
Akshay Raut el
¿Alguien sabe cómo esto desde las funciones de flecha? stackoverflow.com/questions/60015693/…
Thomas Segato
@AkshayRaut IMO es un buen caso de uso: un formulario de propósito general con funciones de reinicio y envío, el último de los cuales devuelve los valores del formulario.
Julian K

Respuestas:

702

En primer lugar, permítanme expresar que, en general, esta no es la forma de hacer las cosas en React land. Por lo general, lo que desea hacer es transmitir la funcionalidad a los niños en accesorios y pasar notificaciones de los niños en eventos (o mejor aún:) dispatch.

Pero si debe exponer un método imperativo en un componente secundario, puede usar referencias . Recuerde que esta es una escotilla de escape y generalmente indica que hay un mejor diseño disponible.

Anteriormente, las referencias solo eran compatibles con componentes basados ​​en la clase. Con el advenimiento de React Hooks , ese ya no es el caso

Uso de ganchos y componentes de función ( >= [email protected])

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 documentación para useImperativeHandle()está aquí :

useImperativeHandlepersonaliza el valor de instancia que se expone a los componentes principales cuando se usa ref.

Usar componentes de clase ( >= [email protected])

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 heredada ( <= [email protected])

Para fines históricos, este es el estilo basado en la devolución de llamada que usaría con las versiones React anteriores a 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>

rossipedia
fuente
23
Estoy cansado, pero termino con este error "_this2.refs.child.getAlert no es una función"
N8FURY
22
Esto se debe a que connectdevuelve un componente de orden superior que envuelve su instancia original. Deberá llamar getWrappedInstance()primero al componente conectado para obtener su componente original. Entonces puede llamar a métodos de instancia sobre eso.
rossipedia
16
Este no es realmente un buen patrón. Sin mencionar que las referencias de cuerdas están mal vistas. Es mejor pasar accesorios al componente hijo y luego hacer clic en el botón para cambiar el estado del padre, y pasar un elemento de estado al niño que activará el niño componentWillReceivePropsy lo usará como disparador.
ffxsam
8
No, generalmente no es el mejor patrón, es más como una escotilla de escape cuando lo necesita, y debe usarse solo en emergencias. Además, esta respuesta se escribió cuando todavía existían referencias de cadena, y tiene razón en que no son la forma "correcta" de hacer las cosas en estos días.
rossipedia
35
Si la mejor práctica es crear un laberinto de lógica para hacer algo tan simple como llamar al método de un componente secundario, entonces no estoy de acuerdo con la mejor práctica.
aaaaaa
150

Puedes usar otro patrón aquí:

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

Lo que hace es establecer el clickChildmétodo del padre cuando se monta el hijo. De esta manera, cuando hace clic en el botón en padre, llamará a clickChildqué llamadasgetAlert .

Esto también funciona si su hijo está envuelto connect()para que no necesitegetWrappedInstance() hack.

Tenga en cuenta que no puede usar onClick={this.clickChild}en padre porque cuando el padre se representa, el hijo no está montado, por this.clickChildlo que aún no está asignado. Usar onClick={() => this.clickChild()}está bien porque cuando haces clic en el botón this.clickChildya debería estar asignado.

ladrillo
fuente
55
Entiendo _this2.clickChild is not a functionpor qué?
tatsu
1
no importa, esto funcionó para mí: github.com/kriasoft/react-starter-kit/issues/…
tatsu
55
ninguno funcionó. solo esta respuesta funcionó: github.com/kriasoft/react-starter-kit/issues/…
tatsu
2
Esta es una técnica interesante. Está bastante limpio y no parece romper ninguna regla. Pero sí creo que su respuesta sería más completa (y cumpliría con las expectativas) si agregara un enlace. Me gustó tanto la respuesta que la publiqué en este tema relacionado de Github .
joeytwiddle
8
Esta debería ser la respuesta aceptada
Jesus Gomez
28

https://facebook.github.io/react/tips/expose-component-functions.html para obtener más respuestas, consulte aquí Métodos de llamada en componentes React child

Al examinar las referencias del componente "razón", está rompiendo la encapsulación y haciendo imposible refactorizar ese componente sin examinar cuidadosamente todos los lugares donde se utiliza. Debido a esto, recomendamos encarecidamente tratar las referencias como privadas para un componente, al igual que el estado.

En general, los datos deben pasarse por el árbol a través de accesorios. Hay algunas excepciones a esto (como llamar a .focus () o activar una animación única que realmente no "cambia" el estado), pero cada vez que expones un método llamado "set", los accesorios generalmente son Una mejor opción. Intente que el componente de entrada interno se preocupe por su tamaño y apariencia para que ninguno de sus antepasados ​​lo haga.

Mike Tronic
fuente
55
Aquí está la fuente de esta respuesta: discusion.reactjs.org/t/… . No hay problemas para citar a otros, pero al menos pon alguna referencia.
Jodo
1
¿Cómo exactamente esto rompe la encapsulación más que los accesorios?
Timmmm
17

Método alternativo con useEffect:

Padre:

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

Niños:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);
tonymayoral
fuente
2
Esto debería obtener más votos
Ali Al Amine
PD. Si su deseo es simplemente volver a representar el formulario (por ejemplo, para restablecer los campos de entrada), entonces ni siquiera necesita incluir useEffect, solo puede hacer que el accesorio se envíe al cambio de componente
Matt Fletcher
8

Podemos usar referencias de otra manera como-

Vamos a crear un elemento primario, que representará un <Child/>componente. Como puede ver, el componente que se representará, debe agregar el atributo ref y proporcionarle un nombre.
Luego, la triggerChildAlertfunción, ubicada en la clase padre, accederá a la propiedad refs de este contexto (cuando triggerChildAlertse activa la función accederá a la referencia secundaria y tendrá todas las funciones del elemento secundario).

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

Ahora, el componente hijo, como se diseñó teóricamente anteriormente, se verá así:

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

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

Aquí está el código fuente: ¡
Hope te ayudará!

S.Yadav
fuente
1
Las referencias de cadena están en desuso. reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy
4

Si está haciendo esto simplemente porque quiere que el Niño proporcione un rasgo reutilizable a sus padres, entonces puede considerar hacerlo usando accesorios de renderizado .

Esa técnica realmente pone la estructura al revés. El Childahora envuelve al padre, por lo que lo he renombrado a AlertTraitcontinuación. Mantuve el nombre Parentde continuidad, aunque en realidad no es un padre ahora.

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

En este caso, AlertTrait proporciona uno o más rasgos que pasa como accesorios a cualquier componente que se le haya dado en su renderComponent accesorio.

El padre recibe doAlert como accesorio y puede llamarlo cuando sea necesario.

(Para mayor claridad, llamé al accesorio renderComponenten el ejemplo anterior. Pero en los documentos React vinculados anteriormente, simplemente lo llamanrender ).

El componente Trait puede representar cosas que rodean al elemento primario, en su función de representación, pero no representa nada dentro del elemento primario. En realidad, podría representar cosas dentro del padre, si pasara otro accesorio (por ejemplo,renderChild ) al padre, que el padre podría usar durante su método de renderizado.

Esto es algo diferente de lo que solicitó el OP, pero algunas personas podrían terminar aquí (como lo hicimos nosotros) porque querían crear un rasgo reutilizable, y pensaron que un componente hijo era una buena manera de hacerlo.

joeytwiddle
fuente
Aquí hay una lista útil de patrones para crear rasgos reutilizables
joeytwiddle
¿Qué pasa si tiene N cronómetros y un botón para reiniciarlos a todos? ¿Cómo se hacen útiles los accesorios aquí?
vsync
@vsync No estoy seguro de que este método pueda ayudarlo en su tarea. Pero la respuesta de brickingup podría ayudar. Tenga en cuenta que se configuran, this.clickChild = clickpero sus múltiples cronómetros pasarían múltiples funciones, por lo que necesitaría almacenarlos todos:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle
1

Puede lograr esto fácilmente de esta manera

Pasos-

  1. Cree una variable booleana en el estado de la clase primaria. Actualice esto cuando desee llamar a una función.
  2. Cree una variable prop y asigne la variable booleana.
  3. Desde el componente secundario, acceda a esa variable utilizando accesorios y ejecute el método que desee teniendo una condición 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>
        );
       }
    }
Kusal Kithmal
fuente
Es posible que desee sandbox esto. Parece que va a terminar con un bucle infinito porque el método secundario se ejecutará continuamente porque el estado principal se establece en verdadero.
Isaac Pak
@IsaacPak Sí, por eso dejé un comentario allí, diciendo que debes actualizar el estado de acuerdo a tus requerimientos. Entonces no se ejecutará como un bucle infinito.
Kusal Kithmal
1

Estoy usando useEffecthook para superar el dolor de cabeza de hacer todo esto, así que ahora paso una variable a un niño como este:

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

No estaba satisfecho con ninguna de las soluciones presentadas aquí. En realidad, hay una solución muy simple que se puede hacer usando Javascript puro sin depender de alguna funcionalidad React que no sea el objeto de accesorios básicos, y le brinda el beneficio de comunicarse en cualquier dirección (padre -> niño, niño -> padre). Debe pasar un objeto del componente primario al componente secundario. Este objeto es a lo que me refiero como una "referencia bidireccional" o biRef para abreviar. Básicamente, el objeto contiene una referencia a métodos en el padre que el padre quiere exponer. Y el componente hijo adjunta métodos al objeto que el padre puede llamar. Algo como esto:

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

La otra ventaja de esta solución es que puede agregar muchas más funciones en el padre y el hijo mientras las pasa del padre al hijo usando solo una propiedad.

Una mejora sobre el código anterior es no agregar las funciones padre e hijo directamente al objeto biRef, sino más bien a los miembros secundarios. Las funciones primarias deben agregarse a un miembro llamado "primario", mientras que las funciones secundarias deben agregarse a un miembro llamado "secundario".

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

Al colocar las funciones padre e hijo en miembros separados del objeto biRef, tendrá una separación clara entre los dos y verá fácilmente cuáles pertenecen al padre o hijo. También ayuda a evitar que un componente secundario sobrescriba accidentalmente una función principal si la misma función aparece en ambas.

Una última cosa es que si observa que el componente primario crea el objeto biRef con var, mientras que el componente secundario accede a él a través del objeto props. Puede ser tentador no definir el objeto biRef en el padre y acceder a él desde su padre a través de su propio parámetro de accesorios (que podría ser el caso en una jerarquía de elementos de la interfaz de usuario). Esto es arriesgado porque el niño puede pensar que una función que está llamando al padre pertenece al padre cuando en realidad podría pertenecer a un abuelo. No hay nada de malo en esto, siempre y cuando lo sepas. A menos que tenga una razón para apoyar alguna jerarquía más allá de una relación padre / hijo, es mejor crear biRef en su componente padre.

AndroidDev
fuente
0

Creo que la forma más básica de llamar a los métodos es estableciendo una solicitud en el componente secundario. Luego, tan pronto como el niño maneja la solicitud, llama a un método de devolución de llamada para restablecer la solicitud.

El mecanismo de reinicio es necesario para poder enviar la misma solicitud varias veces una tras otra.

En componente principal

En el método de representación del padre:

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

El padre necesita 2 métodos, para comunicarse con su hijo en 2 direcciones.

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

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

En componente hijo

El niño actualiza su estado interno, copiando la solicitud de los accesorios.

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

Luego, finalmente maneja la solicitud y envía el restablecimiento al padre:

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

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

Otra forma de activar una función secundaria desde el elemento primario es hacer uso de la componentDidUpdatefunción en Componente secundario. Le paso un accesorio triggerChildFuncde padre a hijo, que inicialmente es null. El valor cambia a una función cuando se hace clic en el botón y Child nota que ese cambio componentDidUpdatey llama a su propia función interna.

Como prop triggerChildFunccambia a una función, también recibimos una devolución de llamada a Parent. Si Parent no necesita saber cuándo se llama a la función, el valor triggerChildFuncpodría, por ejemplo, cambiar de 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

Calsal
fuente
0

Aquí mi demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Estoy usando useEffectpara llamar a los métodos del componente hijos. Lo he intentado, Proxy and Setter_Getterpero hasta ahora useEffectparece ser la forma más conveniente de llamar a un método hijo desde el padre. Para usarlo Proxy and Setter_Getter, parece que hay algo de sutileza que superar primero, porque el elemento representado en primer lugar es un elemento similar a un objeto a través de la ref.current return => <div/>especificidad de. Con respecto a esto useEffect, también puede aprovechar este enfoque para establecer el estado de los padres dependiendo de lo que quiera hacer con los hijos.

En el enlace de la demostración que he proporcionado, encontrará el código completo de mi ReactJS con mi dibujo interno para que pueda apreciar el flujo de trabajo de mi solución.

Aquí le proporciono el fragmento de mi ReactJS solo con el código relevante. :

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>
       )
}
Webwoman
fuente
0

Estamos contentos con un gancho personalizado que llamamos useCounterKey. Simplemente configura una counterKey, o una clave que cuenta desde cero. La función que devuelve restablece la tecla (es decir, incremento). (Creo que esta es la forma más idiomática en React para restablecer un componente, solo golpee la tecla).

Sin embargo, este enlace también funciona en cualquier situación en la que desee enviar un mensaje único al cliente para que haga algo. Por ejemplo, lo usamos para enfocar un control en el niño en un determinado evento padre; solo se enfoca automáticamente cada vez que se actualiza la clave. (Si se necesitan más accesorios, se pueden configurar antes de restablecer la clave para que estén disponibles cuando ocurra el evento).

Este método tiene un poco de una curva de aprendizaje b / c, no es tan sencillo como un controlador de eventos típico, pero parece la forma más idiomática de manejar esto en React que hemos encontrado (ya que las teclas ya funcionan de esta manera). Definitivamente abierto a comentarios sobre este método, pero está funcionando bien.

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

Usos de muestra:

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

  // ...
}

(Crédito a Kent Dodds por el concepto counterKey ).

Freewalker
fuente
-1

Aquí hay un error? a tener en cuenta: estoy de acuerdo con la solución de rossipedia usando forwardRef, useRef, useImperativeHandle

Hay cierta información errónea en línea que dice que las referencias solo se pueden crear a partir de los componentes React Class, pero de hecho puedes usar Function Components si usas los ganchos mencionados anteriormente. Una nota, los ganchos solo funcionaron para mí después de que cambié el archivo para no usar withRouter () al exportar el componente. Es decir, un cambio de

export default withRouter(TableConfig);

en lugar de ser

export default TableConfig;

En retrospectiva, withRouter () no es necesario para dicho componente de todos modos, pero por lo general no hace daño tenerlo. Mi caso de uso es que creé un componente para crear una Tabla para manejar la visualización y edición de valores de configuración, y quería poder decirle a este componente secundario que restablezca sus valores de estado cada vez que se presiona el botón Restablecer del formulario principal. UseRef () no obtendría adecuadamente ref o ref.current (seguía siendo nulo) hasta que elimine withRouter () del archivo que contiene mi componente hijo TableConfig

DeltaPng
fuente