Вызовите дочерний метод от родителя

475

У меня есть два компонента.

  1. Родительский компонент
  2. Дочерний компонент

Я пытался вызвать метод ребенка из Parent, я пытался таким образом, но не смог получить результат

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

Есть ли способ вызвать метод ребенка от родителя?

Примечание. Дочерний и родительский компоненты находятся в двух разных файлах.

N8FURY
источник
Хотя я очень опаздываю на это, я также изучаю React, поэтому мне хотелось бы узнать, в каком случае родитель должен вызывать дочерний метод. Не могли бы вы объяснить?
Акшай Раут
Кто-нибудь знает, как это из стрелочных функций? stackoverflow.com/questions/60015693/…
Томас Сегато
@AkshayRaut IMO хороший вариант использования: форма общего назначения с функциями reset и submit, последняя из которых возвращает значения формы.
Джулиан К

Ответы:

702

Прежде всего, позвольте мне выразить, что это вообще не тот способ, которым можно заниматься в React Land. Обычно вы хотите передать функциональность дочерним элементам в подпорках и передать уведомления от дочерних элементов в событиях (или еще лучше:) dispatch.

Но если вам необходимо предоставить обязательный метод для дочернего компонента, вы можете использовать ссылки . Помните, что это аварийный люк, который обычно указывает на лучший дизайн.

Ранее ссылки поддерживались только для компонентов на основе классов. С появлением React Hooks это уже не так

Использование хуков и функциональных компонентов ( >= 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>

Документация useImperativeHandle()находится здесь :

useImperativeHandleнастраивает значение экземпляра, которое предоставляется родительским компонентам при использовании ref.

Использование компонентов класса ( >= 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 ( <= react@16.3)

Для исторических целей, вот стиль на основе обратного вызова, который вы бы использовали с версиями React до 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
источник
23
Я устал, но в конечном итоге с этой ошибкой "_this2.refs.child.getAlert не является функцией"
N8FURY
22
Это потому, что connectвозвращает компонент более высокого порядка, который оборачивает ваш оригинальный экземпляр. getWrappedInstance()Сначала вам нужно вызвать подключенный компонент, чтобы получить исходный компонент. Затем вы можете вызвать методы экземпляра на этом.
Россипедия
16
Это не очень хорошая модель. Не говоря уже о ссылках на строки. Лучше передать реквизиты в дочерний компонент, а затем нажать кнопку в родительском компоненте, чтобы изменить состояние родителя, и передать элемент состояния в дочерний элемент, который вызовет дочерний компонент componentWillReceiveProps, и использовать его в качестве триггера.
ffxsam
8
Нет, обычно это не лучший шаблон, это скорее запасной люк, когда он вам нужен, и его следует использовать только в чрезвычайных ситуациях. Кроме того, этот ответ был написан, когда ссылки на строки все еще были рядом, и вы правы, что в наши дни это не «правильный» способ сделать что-то.
россияпия
35
Если передовой практикой является создание лабиринта логики для выполнения чего-либо столь же простого, как вызов метода дочернего компонента, - тогда я не согласен с передовой практикой.
аааааа
150

Вы можете использовать другой шаблон здесь:

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

Что он делает, так это устанавливает метод родителя clickChildпри монтировании child. Таким образом, когда вы нажимаете кнопку в родительском, он будет вызывать, clickChildкоторый вызывает ребенка getAlert.

Это также работает, если ваш ребенок обернут, connect()так что вам не нужен getWrappedInstance()взлом.

Обратите внимание, что вы не можете использовать onClick={this.clickChild}в родительском, потому что когда родительский отображается, дочерний не подключен, поэтому this.clickChildеще не назначен. Использование onClick={() => this.clickChild()}хорошо, потому что при нажатии кнопки this.clickChildуже должны быть назначены.

brickingup
источник
5
Я понял _this2.clickChild is not a functionпочему?
Tatsu
1
фигу это работало для меня: github.com/kriasoft/react-starter-kit/issues/...
Tatsu
5
ни работал. только этот ответ работал: github.com/kriasoft/react-starter-kit/issues/...
Tatsu
2
Это интересная техника. Он довольно чистый и не нарушает никаких правил. Но я думаю, что ваш ответ будет более полным (и соответствует ожиданиям), если вы добавите привязку. Мне так понравился ответ, что я опубликовал его по этой связанной проблеме Github .
Joeytwiddle
8
Это должен быть ответ на этот вопрос
Иисус Гомес
28

https://facebook.github.io/react/tips/expose-component-functions.html для получения дополнительных ответов см. здесь Вызов методов на дочерних компонентах React

Изучая ссылки на компонент «причина», вы нарушаете инкапсуляцию и делаете невозможным рефакторинг этого компонента без тщательного изучения всех мест его использования. По этой причине мы настоятельно рекомендуем рассматривать ссылки как частные для компонента, так же как и для состояния.

В общем, данные должны передаваться по дереву через реквизит. Есть несколько исключений из этого (например, вызов .focus () или запуск одноразовой анимации, которая на самом деле не «меняет» состояние), но каждый раз, когда вы выставляете метод с именем «set», реквизит обычно лучший выбор. Постарайтесь сделать так, чтобы внутренний компонент ввода беспокоился о своем размере и внешнем виде, чтобы никто из его предков не беспокоился.

Майк Троник
источник
5
Вот источник этого ответа: обсуждения.reactjs.org/t/… . Нет проблем с цитированием других, но, по крайней мере, вставьте некоторые ссылки.
Jodo
1
Как именно это нарушает инкапсуляцию больше, чем реквизит?
Тимммм
18

Альтернативный метод с useEffect:

родитель:

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

Дети:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);
tonymayoral
источник
3
Это должно набрать больше голосов
Али Аль Амин
Ps. Если вы хотите просто перерисовать форму (например, для сброса полей ввода), вам даже не нужно включать useEffect, вы можете просто сделать отправку реквизита в изменение компонента
Мэтт Флетчер
8

Мы можем использовать ссылки другим способом, так как

Мы собираемся создать родительский элемент, он будет визуализировать <Child/>компонент. Как видите, для компонента, который будет отображаться, нужно добавить атрибут ref и указать для него имя.
Затем triggerChildAlertфункция, расположенная в родительском классе, получит доступ к свойству refs этого контекста (когда triggerChildAlertфункция сработает, она получит доступ к дочерней ссылке и будет иметь все функции дочернего элемента).

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

Теперь дочерний компонент, как теоретически разработано ранее, будет выглядеть так:

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

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

Здесь исходный код -
Надежда поможет вам!

S.Yadav
источник
1
Строковые ссылки устарели. actjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Кори МакЭбой
4

Если вы делаете это просто потому, что хотите, чтобы Ребенок предоставил своим родителям повторно используемую черту, то вы можете подумать об этом, используя render-props вместо этого .

Эта техника фактически переворачивает конструкцию с ног на голову. ChildТеперь оборачивает родитель, поэтому я переименовал его AlertTraitниже. Я сохранил название Parentдля преемственности, хотя сейчас это не совсем родитель.

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

В этом случае AlertTrait предоставляет одну или несколько черт, которые он передает в качестве реквизита для любого компонента, который ему был дан в его renderComponent реквизите.

Родитель получает doAlert в качестве опоры и может вызывать ее при необходимости.

(Для ясности я назвал опору renderComponentв вышеприведенном примере. Но в связанных с React документах они просто называют этоrender .)

Компонент Trait может отображать вещи, окружающие Parent, в своей функции render, но он ничего не отображает внутри родительского элемента. На самом деле это может сделать вещи внутри Родителя, если он прошел другую опору (например,renderChild ) родительскому объекту, который родительский объект мог бы затем использовать во время своего метода render.

Это несколько отличается от того, о чем просил OP, но некоторые люди могут оказаться здесь (как мы), потому что они хотели создать черту многократного использования и думали, что дочерний компонент - хороший способ сделать это.

joeytwiddle
источник
Здесь есть удобный список шаблонов для создания черт многократного использования : actjs.org/blog/2016/07/13/…
joeytwiddle
Что делать, если у вас есть N секундомеров и одна кнопка, чтобы перезапустить их все. Как сделать реквизит здесь удобным?
vsync
@vsync Я не уверен, что этот метод может помочь для вашей задачи. Но ответ кирпича может помочь. Обратите внимание, что они установлены, this.clickChild = clickно ваши несколько секундомеров будут передавать несколько функций, поэтому вам нужно будет сохранить все из них:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle
1

Вы можете легко достичь этого

Steps-

  1. Создайте логическую переменную в состоянии в родительском классе. Обновите это, когда вы хотите вызвать функцию.
  2. Создайте переменную prop и присвойте логическую переменную.
  3. Из дочернего компонента обращайтесь к этой переменной с помощью props и выполняйте нужный метод, используя условие 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>
        );
       }
    }
Кусал Китмал
источник
Вы могли бы хотеть поместить в песочницу это. Похоже, что вы собираетесь закончить бесконечным циклом, потому что дочерний метод будет постоянно выполняться, потому что родительское состояние установлено в true.
Исаак Пак
@IsaacPak Да, именно поэтому я оставил там комментарий, в котором говорится, что вы должны обновить состояние в соответствии с вашими требованиями. Тогда он не будет работать как бесконечный цикл.
Кусал Китмал
1

Я использую useEffectловушку, чтобы преодолеть головную боль при выполнении всего этого, поэтому теперь я передаю переменную child так:

<ParentComponent>
 <ChildComponent arbitrary={value} />
</ParentComponent>
useEffect(() => callTheFunctionToBeCalled(value) , [value]);
Черная мамба
источник
1

Я не был удовлетворен ни одним из решений, представленных здесь. На самом деле существует очень простое решение, которое может быть сделано с использованием чистого Javascript, не полагаясь на некоторые функциональные возможности React, кроме базового объекта props, и дает вам преимущество общения в любом направлении (parent -> child, child -> parent). Вам необходимо передать объект из родительского компонента в дочерний компонент. Этот объект - это то, что я называю «двунаправленной ссылкой» или для краткости biRef. По сути, объект содержит ссылку на методы в родительском объекте, которые родитель хочет предоставить. И дочерний компонент присоединяет методы к объекту, который родитель может вызвать. Что-то вроде этого:

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

Другое преимущество этого решения заключается в том, что вы можете добавить намного больше функций в родительский и дочерний процесс, передавая их от родительского к дочернему элементу, используя только одно свойство.

Улучшение по сравнению с приведенным выше кодом заключается в том, что родительские и дочерние функции добавляются не непосредственно к объекту biRef, а к подчиненным элементам. Родительские функции должны быть добавлены к члену с именем «parent», тогда как дочерние функции должны быть добавлены к члену с именем «child».

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

Размещая родительские и дочерние функции в отдельных членах объекта biRef, вы получите четкое разделение между ними и легко увидите, какие из них принадлежат родительскому или дочернему объекту. Это также помогает предотвратить случайную перезапись дочерним компонентом родительской функции, если одна и та же функция появляется в обеих.

И последнее: если вы заметите, родительский компонент создает объект biRef с помощью var, тогда как дочерний компонент обращается к нему через объект props. Может быть соблазнительно не определять объект biRef в родительском объекте и обращаться к нему от его родительского объекта через собственный параметр props (что может иметь место в иерархии элементов пользовательского интерфейса). Это рискованно, потому что ребенок может подумать, что функция, которую он вызывает у родителя, принадлежит родителю, когда он на самом деле может принадлежать дедушке. В этом нет ничего плохого, если вы знаете об этом. Если у вас нет причин для поддержки какой-либо иерархии, выходящей за рамки родительских и дочерних отношений, лучше всего создать biRef в родительском компоненте.

AndroidDev
источник
0

Я думаю, что самый простой способ вызова методов - это установить запрос на дочерний компонент. Затем, как только ребенок обрабатывает запрос, он вызывает метод обратного вызова для сброса запроса.

Механизм сброса необходим, чтобы иметь возможность отправлять один и тот же запрос несколько раз друг за другом.

В родительском компоненте

В методе рендеринга родителя:

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

Родителю нужно 2 метода, чтобы общаться с ребенком в 2 направлениях.

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

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

В дочернем компоненте

Ребенок обновляет свое внутреннее состояние, копируя запрос из реквизита.

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

Затем, наконец, он обрабатывает запрос и отправляет сброс родителю:

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

  const { onRequestHandled } = this.props;
  if (onRequestHandled != null) onRequestHandled();
}
bvdb
источник
0

Еще один способ вызвать дочернюю функцию из родительского - использовать componentDidUpdateфункцию в дочернем компоненте. Я передаю реквизит triggerChildFuncот Родителя Ребенку, который изначально есть null. Значение изменяется на функцию при нажатии кнопки, и дочерний элемент замечает, что он изменяется, componentDidUpdateи вызывает свою собственную внутреннюю функцию.

Поскольку prop triggerChildFuncизменяется на функцию, мы также получаем обратный вызов для Parent. Если Parent не нужно знать, когда вызывается функция, значение triggerChildFuncможет, например, измениться с nullна 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
источник
0

Вот мое демо: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Я использую useEffectдля вызова методов дочернего компонента. Я пытался с, Proxy and Setter_Getterно useEffectкажется, что гораздо более удобный способ вызова дочернего метода из родительского. Чтобы использовать Proxy and Setter_Getterэто, кажется, есть некоторая тонкость, которую нужно преодолеть в первую очередь, потому что элемент, представленный первым, является элементом objectLike через ref.current return => <div/>специфику. Что касается useEffect, вы также можете использовать этот подход для установки состояния родителя в зависимости от того, что вы хотите делать с детьми.

В предоставленной мною демонстрационной ссылке вы найдете мой полный код ReactJS с моим черновиком внутри, чтобы вы могли оценить рабочий процесс моего решения.

Здесь я предоставляю вам свой фрагмент кода ReactJS только с соответствующим кодом. :

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
источник
0

Мы счастливы с пользовательским крючком, который мы называем useCounterKey. Он просто устанавливает counterKey или ключ, который отсчитывает от нуля. Функция, которую она возвращает, сбрасывает ключ (то есть увеличивает). (Я считаю, что это самый идиоматичный способ сброса компонента в React - просто нажмите клавишу).

Однако этот хук также работает в любой ситуации, когда вы хотите отправить одноразовое сообщение клиенту, чтобы что-то сделать. Например, мы используем его, чтобы сфокусировать элемент управления в дочернем элементе на определенном родительском событии - он просто автоматически фокусируется при каждом обновлении ключа. (Если требуется больше реквизита, их можно установить до сброса ключа, чтобы они были доступны при возникновении события.)

Этот метод имеет некоторую кривую обучения, поскольку он не так прост, как типичный обработчик событий, но кажется наиболее идиоматичным способом обработки этого в React, который мы нашли (поскольку ключи уже функционируют таким образом). Def открыт для отзывов об этом методе, но он работает хорошо!

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

Примеры использования:

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

  // ...
}

( Благодарим Кента Доддса за концепцию counterKey .)

Freewalker
источник
-1

Вот ошибка? на что обратить внимание: я согласен с решением Россипедии, используя forwardRef, useRef, useImperativeHandle

В Интернете есть некоторая дезинформация о том, что ссылки могут создаваться только из компонентов класса React, но вы действительно можете использовать функциональные компоненты, если используете вышеупомянутые хуки выше. Обратите внимание, что ловушки работали для меня только после того, как я изменил файл, чтобы не использовать withRouter () при экспорте компонента. Т.е. изменение от

export default withRouter(TableConfig);

вместо того, чтобы быть

export default TableConfig;

Оглядываясь назад, метод withRouter () в любом случае не нужен для такого компонента, но обычно он ничем не мешает. В моем случае я создал компонент для создания таблицы, предназначенной для просмотра и редактирования значений конфигурации, и я хотел, чтобы этот дочерний компонент мог сбросить значения своего состояния при каждом нажатии кнопки «Сброс» родительской формы. UseRef () не получал должным образом ref или ref.current (продолжал получать значение null), пока я не удалил withRouter () из файла, содержащего мой дочерний компонент TableConfig

DeltaPng
источник