Виклик дочірнього методу від батьків


474

У мене є дві складові.

  1. Батьківський компонент
  2. Дочірній компонент

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

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

Чи є спосіб викликати дитячий метод від батьків?

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


Хоча я дуже спізнююсь із цим, я також вивчаю Реагу, тому хотів би дізнатися про випадок, коли батькові потрібно було б викликати дочірній метод. Будь ласка, поясніть?
Акшай Раут

Хтось знає, як це зробити із функцій стрілки? stackoverflow.com/questions/60015693/…
Томас Сегато

@AkshayRaut IMO - корисний випадок використання: форма загального призначення з функціями скидання та подання, пізніша з яких повертає значення форми.
Джуліан К

Відповіді:


702

По-перше, дозвольте мені висловити, що це, як правило, не шлях до речей на землі Реакти. Зазвичай те, що ви хочете зробити, - це передавати функціональність дітям у реквізиті та передавати сповіщення від дітей у заходах (а ще краще:) 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>

Legacy 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>


23
Я втомився, але закінчую цю помилку "_this2.refs.child.getAlert - це не функція"
N8FURY

22
Це тому, що connectповертає компонент вищого порядку, який обгортає ваш початковий екземпляр. Вам потрібно спочатку зателефонувати getWrappedInstance()на підключений компонент, щоб отримати оригінальний компонент. Тоді ви можете викликати методи екземпляра на цьому.
rossipedia

16
Це насправді не вдалий зразок. Не кажучи вже про строкові посилання нахмурилися. Краще передати реквізит у дочірній компонент, а потім натиснути кнопку, щоб батько змінив стан батьків, і передав елемент стану дитині, який запустить дитину componentWillReceiveProps, і використовувати це як тригер.
ffxsam

7
Ні, це, як правило, не найкращий зразок, це скоріше евакуаційний люк, коли вам це потрібно, і його слід використовувати лише в надзвичайних ситуаціях. Крім того, ця відповідь була написана, коли рефлекторні рядки все ще були навколо, і ти маєш рацію, що в цей час вони не є "правильним" способом.
rossipedia

35
Якщо найкраща практика - це створити лабіринт логіки, щоб зробити щось таке ж просто, як викликати метод дочірнього компонента - тоді я не згоден з найкращою практикою.
аааааа

149

Тут ви можете використовувати інший зразок:

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метод, коли дитина встановлена. Таким чином, при натисканні на кнопку в батьківській програмі він буде називати, clickChildхто дзвонить дитині getAlert.

Це також спрацьовує, якщо ваша дитина загорнутий, connect()щоб вам не потрібен getWrappedInstance()злом.

Зверніть увагу, що ви не можете використовувати onClick={this.clickChild}в батьківській програмі, тому що, коли батько надається, дитина ще не встановлена, тому this.clickChildще не призначена. Використовувати onClick={() => this.clickChild()}це добре, оскільки при натисканні на кнопку this.clickChildвже слід призначити.


5
Я розумію, _this2.clickChild is not a functionчому?
тацу


5
ні працювали. тільки ця відповідь спрацювала: github.com/kriasoft/react-starter-kit/isissue/…
tatsu

2
Це цікава техніка. Він досить чистий і, здається, не порушує жодних правил. Але я думаю, що ваша відповідь була б більш повною (і виправдала б очікування), якби ви додали прив'язку. Відповідь мені так сподобалась, що я опублікував її в цьому спорідненому питанні Github .
joeytwiddle

7
Це має бути прийнята відповідь
Ісус Гомес

27

https://facebook.github.io/react/tips/expose-component-functions.html для отримання додаткових відповідей див. тут Методи виклику на компонентах React child

Дивлячись на рефлексивні компоненти компонента "причина", ви порушуєте інкапсуляцію і унеможливлюєте повторне змінення цього компонента, не ретельно вивчаючи всі місця, які він використовується. Через це ми наполегливо рекомендуємо трактувати refs як приватний компонент, як державний.

Загалом, дані слід передавати вниз по дереву через реквізити. Є кілька винятків із цього (наприклад, виклик .focus () або запускання одноразової анімації, яка насправді не "змінює" стан), але будь-коли ви відкриваєте метод, який називається "set", реквізит зазвичай кращий вибір. Постарайтеся зробити так, щоб внутрішній вхідний компонент турбувався про його розмір і зовнішній вигляд, щоб ніхто з його предків не робив.


5
Ось джерело цієї відповіді: обговорити.reactjs.org/t/… . Немає проблем із цитуванням інших, але, принаймні, посилається на якусь посилання.
Джодо

1
Як саме ця перерва інкапсуляції більше, ніж реквізит?
Timmmm

15

Альтернативний метод з використаннямEffect:

Батько:

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

Діти:

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

2
Це повинно отримати більше голосів
Алі Аль Амін

Пс. Якщо ваше бажання просто повернути форму (наприклад, скинути поля введення), вам навіть не потрібно включати 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>
        );
    }
}

Ось вихідний код -
Надія допоможе вам!


1
Строкові рефлексиви застарілі. reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy

4

Якщо ви робите це просто тому, що хочете, щоб дитина надала своїм батькам ознаку, яка може бути повторно використана, тоді ви можете розглянути це, використовуючи рендерінг замість цього.

Ця техніка насправді перевертає структуру догори дном. 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 може відображати речі, що оточують Батько, у своїй функції візуалізації, але він нічого не відображає всередині батьківського. Насправді він міг би відображати речі всередині Батька, якби передав іншу опору (наприклад,renderChild ) батькові, який потім батько міг би використовувати під час свого методу візуалізації.

Це дещо відрізняється від того, про що вимагала ОП, але деякі люди можуть закінчитися тут (як і ми), тому що вони хотіли створити ознаку багаторазового використання і вважали, що дочірній компонент - це хороший спосіб зробити це.


Тут є зручний список шаблонів для створення повторно використовуваних ознак тут: reactjs.org/blog/2016/07/13/…
joeytwiddle

Що робити, якщо у вас N секундомір і одна кнопка, щоб перезапустити їх усі. Як тут рендеринг корисний?
vsync

@vsync Я не впевнений, що цей метод може допомогти у вашому завданні. Але відповідь на перебіг може допомогти. Зауважте, що вони встановлені, this.clickChild = clickале ваші кілька секундомірів передають кілька функцій, тому вам потрібно буде зберігати їх усі:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle

1

Це можна легко досягти цим способом

Кроки-

  1. Створіть булеву змінну в стані в батьківському класі. Оновіть це, коли потрібно викликати функцію.
  2. Створіть опорну змінну та призначте булеву змінну.
  3. З дочірнього компонента відкрийте цю змінну за допомогою реквізиту та виконайте потрібний метод, маючи умову 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гачок, щоб подолати головний біль, роблячи все це, тому тепер передаю змінну вниз дитині так:

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

1

Я не був задоволений жодним із запропонованих тут рішень. Насправді є дуже просте рішення, яке можна зробити за допомогою чистого Javascript, не покладаючись на якусь функціональність React, крім основного об'єкта реквізиту, - і це дає перевагу спілкування в будь-якому напрямку (батько -> дитина, дитина -> батько). Вам потрібно передати об’єкт від батьківського компонента до дочірнього компонента. Цей об'єкт - це те, що я називаю "двонаправленим посиланням" або коротко 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 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, тоді як дочірній компонент отримує доступ до нього через об'єкт реквізиту. Це може бути заманливо не визначати об'єкт biRef у батьківському доступі та отримувати доступ до нього з його батьківського через власний параметр реквізиту (що може бути в ієрархії елементів інтерфейсу). Це ризиковано, оскільки дитина може подумати, що функція, яку він викликає, належить батькові до батьків, коли вона фактично може належати бабусі та дідусі. У цьому немає нічого поганого, якщо ви це усвідомлюєте. Якщо у вас немає причин підтримувати якусь ієрархію поза межами стосунків батько / дитина, краще створити biRef у своєму батьківському компоненті.



0

Я думаю, що найосновніший спосіб викликати методи - це встановити запит на дочірній компонент. Потім, як тільки дитина обробляє запит, він викликає метод зворотного дзвінка, щоб скинути запит.

Механізм скидання необхідний, щоб мати можливість надсилати один і той же запит кілька разів один за одним.

У батьківському компоненті

У методі візуалізації батьківського:

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

Батькові потрібні 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();
}

0

Інший спосіб запускати функцію дитини від батьків - це використовувати componentDidUpdateфункцію в дочірньому компоненті. Я передаю опору triggerChildFuncвід батьків до дитини, яка спочатку є null. Значення змінюється на функцію, коли натискається кнопка, і дитина помічає, що змінюється, componentDidUpdateі викликає свою внутрішню функцію.

Оскільки опора triggerChildFuncзмінює функцію, ми також отримуємо зворотний виклик батьків. Якщо 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


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/>специфіку 's. Стосовно цього 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>
       )
}

0

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

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

Цей метод має трохи кривої навчання b / c, він не настільки простий, як типовий обробник подій, але, здається, це найбільш ідіоматичний спосіб впоратися з цим у React, який ми знайшли (оскільки ключі вже функціонують таким чином). Захистіть відкрито для відгуків про цей метод, але він працює добре!

// 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 .)


-1

Ось помилка? на що слід звернути увагу: я погоджуюся з рішенням rossipedia за допомогою forwardRef, useRef, useImperativeHandle

В Інтернеті є дезінформація, в якій говориться, що рефлекси можна створювати лише з компонентів класу React, але ви дійсно можете використовувати функціональні компоненти, якщо використовувати вищезгадані гачки вище. Зауважте, гачки працювали для мене лише після того, як я змінив файл, щоб не використовувати withRouter () при експорті компонента. Тобто зміна від

export default withRouter(TableConfig);

щоб натомість бути

export default TableConfig;

З огляду на те, що в такий спосіб компонент withRouter () взагалі не потрібен, але, як правило, нічого не заважає, маючи його. Моїм випадком використання є те, що я створив компонент для створення таблиці для перегляду та редагування значень конфігурації, і я хотів, щоб я міг сказати цьому дочірньому компоненту, щоб він скинув значення стану, коли натиснути кнопку "Скидання батьків". UseRef () неправильно отримає ref або ref.current (триває отримання нуля), поки я не видалю withRouter () з файлу, що містить дочірній компонент TableConfig

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.