Chamar método filho do pai

474

Eu tenho dois componentes

  1. Componente pai
  2. Componente filho

Eu estava tentando chamar o método da criança de Parent, tentei dessa maneira, mas não consegui um 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>
    );
  }
}

Existe uma maneira de chamar o método da criança dos pais?

Nota: Os componentes filho e pai estão em dois arquivos diferentes

N8FURY
fonte
Embora eu esteja muito atrasado para isso, também estou aprendendo o React, por isso gostaria de saber sobre o caso em que um pai precisaria chamar o método filho. Você poderia explicar?
Akshay Raut
Alguém sabe como fazer isso a partir de funções de seta? stackoverflow.com/questions/60015693/…
Thomas Segato 01/02
@AkshayRaut IMO é um bom caso de uso: um formulário de uso geral com funções de redefinição e envio, o último dos quais retorna os valores do formulário.
Julian K

Respostas:

701

Primeiro, deixe-me expressar que esse geralmente não é o caminho a seguir nas coisas no React Land. Geralmente, o que você deseja fazer é passar a funcionalidade para crianças em objetos e passar notificações de crianças em eventos (ou melhor ainda dispatch:).

Mas se você deve expor um método imperativo em um componente filho, pode usar refs . Lembre-se de que é uma escotilha de escape e geralmente indica que um design melhor está disponível.

Anteriormente, as refs eram suportadas apenas para componentes baseados em classe. Com o advento do React Hooks , esse não é mais o caso

Usando ganchos e componentes de função ( >= [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>

A documentação para useImperativeHandle()está aqui :

useImperativeHandlepersonaliza o valor da instância que é exposta aos componentes pai ao usar ref.

Usando componentes de classe ( >= [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 herdada ( <= [email protected])

Para fins históricos, eis o estilo baseado em retorno de chamada que você usaria nas versões 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
fonte
23
Eu cansado, mas acabar com este erro "_this2.refs.child.getAlert não é uma função"
N8FURY
22
Isso ocorre porque connectretorna um componente de ordem superior que envolve sua instância original. Você precisará chamar getWrappedInstance()o componente conectado primeiro para obter o componente original. Então você pode chamar métodos de instância nisso.
Rossipedia
16
Este não é realmente um bom padrão. Sem mencionar que as referências de string são desaprovadas. É melhor passar adereços para o componente filho e, em seguida, pressionar um botão no pai para alterar o estado do pai e passar um item de estado para o filho que acionará o filho componentWillReceivePropse o usará como gatilho.
Ffxsam 30/03
8
Não, geralmente não é o melhor padrão, é mais uma escotilha de escape quando você precisa e deve ser usado apenas em emergências. Além disso, essa resposta foi escrita quando as referências de string ainda estavam por aí, e você está certo de que elas não são a maneira "correta" de fazer as coisas hoje em dia.
Rossipedia 30/03
35
Se a melhor prática é criar um labirinto de lógica para fazer algo tão simples quanto chamar o método de um componente filho - então eu discordo da melhor prática.
Aaaaaa
149

Você pode usar outro padrão aqui:

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

O que ele faz é definir o clickChildmétodo dos pais quando o filho é montado. Dessa forma, quando você clica no botão em pai, ele chamará o clickChildque chama de filho getAlert.

Isso também funciona se o seu filho estiver envolvido, connect()para que você não precise do getWrappedInstance()hack.

Observe que você não pode usar onClick={this.clickChild}no pai porque, quando o pai é renderizado, o filho não está montado e this.clickChildainda não está designado. Usar onClick={() => this.clickChild()}é bom porque quando você clica no botão this.clickChildjá deve estar atribuído.

brickingup
fonte
5
Eu entendo _this2.clickChild is not a functionporque?
tatsu 6/09/09
1
Nevermind isso funcionou para mim: github.com/kriasoft/react-starter-kit/issues/...
tatsu
5
nem funcionou. somente esta resposta funcionou: github.com/kriasoft/react-starter-kit/issues/…
tatsu
2
Esta é uma técnica interessante. É bastante limpo e não parece quebrar nenhuma regra. Mas acho que sua resposta seria mais completa (e atenderá às expectativas) se você adicionar um vínculo. Gostei tanto da resposta que a publiquei nesta questão relacionada ao Github .
Joeytwiddle
7
Esta deve ser a resposta acepted
Jesus Gomez
27

https://facebook.github.io/react/tips/expose-component-functions.html para obter mais respostas, ref ref aqui Métodos de chamada sobre componentes React child

Ao examinar as referências do componente "reason", você está quebrando o encapsulamento e tornando impossível refatorar esse componente sem examinar cuidadosamente todos os lugares em que é usado. Por esse motivo, é altamente recomendável tratar os árbitros como privados para um componente, bem como o estado.

Em geral, os dados devem ser transmitidos pela árvore por meio de adereços. Existem algumas exceções a isso (como chamar .focus () ou acionar uma animação única que realmente não "altera" o estado), mas sempre que você está expondo um método chamado "conjunto", os adereços geralmente são uma escolha melhor. Tente fazer com que o componente de entrada interno se preocupe com seu tamanho e aparência, para que nenhum de seus ancestrais o faça.

Mike Tronic
fonte
5
Aqui está a fonte desta resposta: discuss.reactjs.org/t/… . Não há problemas em citar outros, mas pelo menos coloque alguma referência.
Jodo
1
Como exatamente esse encapsulamento de interrupção é mais do que adereços?
Timmmm 31/01
17

Método alternativo com useEffect:

Pai:

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

Crianças:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);
tonymayoral
fonte
2
Isso deve gerar mais votos
Ali Al Amine
Ps. Se seu desejo é apenas renderizar novamente o formulário (por exemplo, redefinir os campos de entrada), você nem precisa incluir o useEffect, basta fazer com que o suporte seja enviado para a alteração do componente
Matt Fletcher
8

Podemos usar refs de outra maneira, como

Nós vamos criar um elemento Parent, ele renderizará um <Child/>componente. Como você pode ver, o componente que será renderizado, você precisa adicionar o atributo ref e fornecer um nome para ele.
Então, a triggerChildAlertfunção, localizada na classe pai, acessará a propriedade refs desse contexto (quando a triggerChildAlertfunção for acionada, acessará a referência filho e terá todas as funções do elemento filho).

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

Agora, o componente filho, como teoricamente projetado anteriormente, terá a seguinte aparência:

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

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

Aqui está o código fonte - a
Hope irá ajudá-lo!

S.Yadav
fonte
1
As referências de sequência foram descontinuadas. reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy
4

Se você estiver fazendo isso simplesmente porque deseja que a Criança forneça uma característica reutilizável para seus pais, considere fazer isso usando acessórios de renderização .

Essa técnica realmente vira a estrutura de cabeça para baixo. O Childagora envolve o pai, então eu o renomei para AlertTraitabaixo. Eu mantive o nome Parentde continuidade, embora não seja realmente um pai agora.

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

Nesse caso, o AlertTrait fornece uma ou mais características que ele transmite como adereços para qualquer componente que foi fornecido em seu renderComponent suporte.

O pai recebe doAlert como suporte e pode chamá-lo quando necessário.

(Para maior clareza, chamei o suporte renderComponentno exemplo acima. Mas nos documentos do React vinculados acima, eles chamam derender .)

O componente Trait pode renderizar coisas ao redor do pai, em sua função de renderização, mas não renderiza nada dentro do pai. Na verdade, ele poderia renderizar coisas dentro dos Pais, se passasse por outro objeto (por exemplo,renderChild ) para o pai, que ele poderia usar durante seu método de renderização.

Isso é um pouco diferente do que o OP solicitou, mas algumas pessoas podem acabar aqui (como nós) porque queriam criar uma característica reutilizável e pensavam que um componente filho era uma boa maneira de fazer isso.

joeytwiddle
fonte
Há uma lista útil de padrões para a criação de características reutilizáveis ​​aqui: reactjs.org/blog/2016/07/13/…
joeytwiddle
E se você tiver N cronômetros e um botão para reiniciar todos eles. Como os adereços de renderização são úteis aqui?
vsync
@ vsync Não sei se esse método pode ajudar na sua tarefa. Mas a resposta do brickingup pode ajudar. Nota que definir this.clickChild = click, mas seus vários cronômetros passariam múltiplas funções, assim que você precisa para armazenar todos eles:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle
1

Você pode conseguir isso facilmente dessa maneira

Passos-

  1. Crie uma variável booleana no estado na classe pai. Atualize isso quando desejar chamar uma função.
  2. Crie uma variável prop e atribua a variável booleana.
  3. No componente filho, acesse essa variável usando props e execute o método desejado, tendo uma condição 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
fonte
Você pode querer colocar isso em sandbox. Parece que você terminará com um loop infinito porque o método filho será executado continuamente porque o estado pai está definido como verdadeiro.
Isaac Pak
@IsaacPak Sim, foi por isso que deixei um comentário lá, dizendo que você deve atualizar o estado de acordo com sua exigência. Então não será executado como um loop infinito.
Kusal Kithmal
1

Estou usando o useEffecthook para superar a dor de cabeça de fazer tudo isso, agora passo uma variável para uma criança assim:

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

Não fiquei satisfeito com nenhuma das soluções apresentadas aqui. Na verdade, existe uma solução muito simples que pode ser feita usando Javascript puro, sem depender de alguma funcionalidade do React que não seja o objeto props básico - e oferece o benefício de se comunicar em qualquer direção (pai -> filho, filho -> pai). Você precisa passar um objeto do componente pai para o componente filho. Esse objeto é o que eu chamo de "referência bidirecional" ou biRef, para abreviar. Basicamente, o objeto contém uma referência aos métodos no pai que ele deseja expor. E o componente filho anexa métodos ao objeto que o pai pode chamar. Algo assim:

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

A outra vantagem dessa solução é que você pode adicionar muito mais funções no pai e no filho enquanto as passa do pai para o filho usando apenas uma única propriedade.

Uma melhoria em relação ao código acima é não adicionar as funções pai e filho diretamente ao objeto biRef, mas aos sub-membros. As funções pai devem ser adicionadas a um membro chamado "pai", enquanto as funções filho devem ser adicionadas a um membro chamado "filho".

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

Ao colocar as funções pai e filho em membros separados do objeto biRef, você terá uma separação limpa entre os dois e verá facilmente quais pertencem ao pai ou ao filho. Também ajuda a impedir que um componente filho substitua acidentalmente uma função pai, se a mesma função aparecer em ambos.

Uma última coisa é que, se você observar, o componente pai cria o objeto biRef com var, enquanto o componente filho acessa-o através do objeto props. Pode ser tentador não definir o objeto biRef no pai e acessá-lo de seu pai por meio de seu próprio parâmetro props (que pode ser o caso em uma hierarquia de elementos da interface do usuário). Isso é arriscado, porque a criança pode pensar que uma função que está chamando no pai pertence ao pai quando ele realmente pertence a um avô. Não há nada errado com isso, desde que você esteja ciente disso. A menos que você tenha um motivo para apoiar alguma hierarquia além de um relacionamento pai / filho, é melhor criar o biRef no seu componente pai.

AndroidDev
fonte
0

Eu acho que a maneira mais básica de chamar métodos é definindo uma solicitação no componente filho. Assim que o filho manipula a solicitação, ele chama um método de retorno de chamada para redefinir a solicitação.

O mecanismo de redefinição é necessário para poder enviar a mesma solicitação várias vezes uma após a outra.

No componente pai

No método de renderização do pai:

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

O pai precisa de 2 métodos, para se comunicar com seu filho em 2 direções.

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

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

No componente filho

O filho atualiza seu estado interno, copiando a solicitação dos acessórios.

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

Finalmente, ele lida com a solicitação e envia a redefinição ao pai:

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

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

Outra maneira de acionar uma função filho do pai é fazer uso da componentDidUpdatefunção no Componente filho. Eu passo uma proposta triggerChildFuncde pai para filho, que inicialmente é null. O valor muda para uma função quando o botão é clicado e o Child percebe a alteração componentDidUpdatee chama sua própria função interna.

Como o prop triggerChildFuncmuda para uma função, também recebemos um retorno de chamada para o pai. Se o pai não precisar saber quando a função é chamada, o valor triggerChildFuncpoderá, por exemplo, mudar de nullpara 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
fonte
0

Aqui está minha demonstração: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Estou usando useEffectpara chamar os métodos do componente filho. Eu tentei com, Proxy and Setter_Gettermas agora useEffectparece ser a maneira mais conveniente de chamar um método filho do pai. Para usá- Proxy and Setter_Getterlo, parece que há alguma sutileza a ser superada primeiro, porque o elemento renderizado primeiro é o elemento de um objectLike através da ref.current return => <div/>especificidade do. Com relação a isso useEffect, você também pode aproveitar essa abordagem para definir o estado dos pais, dependendo do que você deseja fazer com os filhos.

No link da demonstração que forneci, você encontrará o código completo do ReactJS com meus rascunhos dentro para que você possa apreciar o fluxo de trabalho da minha solução.

Aqui estou fornecendo a você o snippet do meu ReactJS apenas com o 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
fonte
0

Estamos felizes com um gancho personalizado que chamamos useCounterKey. Ele apenas configura uma counterKey, ou uma chave que conta a partir de zero. A função que ele retorna redefine a tecla (ou seja, incremento). (Acredito que essa é a maneira mais idiomática do React para redefinir um componente - basta pressionar a tecla).

No entanto, esse gancho também funciona em qualquer situação em que você deseja enviar uma mensagem única ao cliente para fazer alguma coisa. Por exemplo, nós o usamos para focar um controle no filho em um determinado evento pai - ele apenas se concentra automaticamente sempre que a chave é atualizada. (Se mais adereços forem necessários, eles poderão ser configurados antes de redefinir a chave para que fiquem disponíveis quando o evento acontecer.)

Esse método tem uma curva de aprendizado b / c, não é tão simples quanto um manipulador de eventos típico, mas parece a maneira mais idiomática de lidar com isso no React que encontramos (já que as teclas já funcionam dessa maneira). Def aberto a feedback sobre esse método, mas está funcionando bem!

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

Exemplos de usos:

// 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 pelo conceito counterKey .)

Freewalker
fonte
-1

Aqui está um bug? a procurar: Concordo com a solução da rossipedia usando forwardRef, useRef, useImperativeHandle

Há algumas informações erradas online que afirmam que refs só podem ser criadas a partir de componentes da classe React, mas você pode realmente usar componentes de função se usar os ganchos acima mencionados. Uma observação: os ganchos só funcionaram para mim depois que eu mudei o arquivo para não usar withRouter () ao exportar o componente. Ou seja, uma mudança de

export default withRouter(TableConfig);

em vez disso ser

export default TableConfig;

Em retrospectiva, o withRouter () não é necessário para esse componente, mas geralmente não prejudica nada que esteja nele. Meu caso de uso é que eu criei um componente para criar uma Tabela para lidar com a visualização e edição dos valores de configuração, e eu queria poder dizer a esse componente filho para redefinir seus valores de estado sempre que o botão Redefinir do formulário pai fosse pressionado. UseRef () não obteria adequadamente o ref ou ref.current (continuando sendo nulo) até que eu removesse withRouter () do arquivo que contém meu componente filho TableConfig

DeltaPng
fonte