9

Вызов метода дочернего класса из родительского

10

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

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

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

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

5 ответ(ов)

2

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

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 уже должен быть присвоен.

1

В вашем коде вы используете useState для хранения счетчика обновлений (refresh), который передается в дочерний компонент Children. Внутри Children вы используете useEffect для выполнения функции performRefresh каждый раз, когда значение refresh изменяется. Это нормальный и распространенный подход для механизмов обновления в React.

Однако, если вы ищете альтернативный метод, вы можете рассмотреть следующие подходы:

1. Использовать Callback

Вместо передачи refresh как пропса, вы можете передать функцию для обновления:

// Parent
const handleRefresh = () => {
    performRefresh(); // вызываем функцию обновления, когда это необходимо
};

<Button onClick={handleRefresh} />
<Children onRefresh={handleRefresh} />

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

2. Избегать лишнего состояния

Если вам не нужно хранить состояние обновления, можно напрямую вызывать функцию обновления из родительского компонента:

// Parent
const performRefresh = () => {
    // Логика обновления, которую можно вынести сюда
};

<Button onClick={performRefresh} />
<Children onRefresh={performRefresh} />

3. Использовать Context для управления состоянием

Если у вас несколько дочерних компонентов, которым нужно использовать функцию обновления, можно использовать Context API для более гибкого управления состоянием:

// Создайте контекст
const RefreshContext = createContext();

const Parent = () => {
    const handleRefresh = () => {
        performRefresh();
    };

    return (
        <RefreshContext.Provider value={handleRefresh}>
            <Button onClick={handleRefresh} />
            <Children />
        </RefreshContext.Provider>
    );
};

// Дочерний компонент
const Children = () => {
    const performRefresh = useContext(RefreshContext);

    useEffect(() => {
        // Логика, которая зависит от состояния
        performRefresh(); 
    }, []);

    return <div>...</div>;
};

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

1

Вот четыре возможные комбинации, которые могут возникнуть:

1. Класс Родитель | Хук Ребенок

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'позвони мне'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

const Child = React.forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('позвони мне')
  }

  return (<View><Text> Я ребенок</Text></View>)
})

2. Хук Родитель | Класс Ребенок

function Parent(props) {
  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'позвони мне'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

class Child extends React.Component {
  childMethod() {
    console.log('позвони мне')
  }

  render() {
    return (<View><Text> Я ребенок</Text></View>)
  }
}

3. Хук Родитель | Хук Ребенок

function Parent(props) {
  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'позвони мне'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

const Child = React.forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('позвони мне')
  }

  return (<View><Text> Я ребенок</Text></View>)
})

4. Класс Родитель | Класс Ребенок

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'позвони мне'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

class Child extends React.Component {
  childMethod() {
    console.log('позвони мне')
  }

  render() {
    return (<View><Text> Я ребенок</Text></View>)
  }
}

Эти примеры показывают, как можно использовать различные подходы к созданию родительского и дочернего компонентов в React, как с помощью классов, так и с использованием хуков. Выбор подхода зависит от ваших предпочтений и требований проекта.

0

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

// Родительский компонент.
function MyParentComponent(props) {
   function someParentFunction() {
      // Этот метод может вызывать дочерний компонент.
   }

   function onButtonClick() {
       // Вызовем функцию из дочернего компонента.
       biRef.someChildFunction();
   }

   // Добавьте все функции здесь, которые может вызывать дочерний компонент.
   var biRef = {
      someParentFunction: someParentFunction
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}

// Дочерний компонент
function MyChildComponent(props) {
   function someChildFunction() {
      // Этот метод может вызывать родительский компонент.
   }

   function onButtonClick() {
      // Вызовем родительскую функцию.
      props.biRef.someParentFunction();
   }

   // Добавьте все функции дочернего компонента в props.biRef, которые хотите,
   // чтобы родитель мог вызывать.
   props.biRef.someChildFunction = someChildFunction;

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

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

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

// Родительский компонент.
function MyParentComponent(props) {
   function someParentFunction() {
      // Этот метод может вызывать дочерний компонент.
   }

   function onButtonClick() {
       // Вызовем функцию из дочернего компонента.
       biRef.child.someChildFunction();
   }

   // Добавьте все функции, которые может вызывать дочерний компонент.
   var biRef = {
      parent: {
          someParentFunction: someParentFunction
      }
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}

// Дочерний компонент
function MyChildComponent(props) {
   function someChildFunction() {
      // Этот метод может вызывать родительский компонент.
   }

   function onButtonClick() {
      // Вызовем родительскую функцию.
      props.biRef.parent.someParentFunction();
   }

   // Добавьте все функции дочернего компонента в props.biRef, которые хотите,
   // чтобы родитель мог вызывать.
   props.biRef.child = {
       someChildFunction: someChildFunction
   };

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

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

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

0

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

import { useEffect, useState } from "react";

export const ParentComponent = () => {
  const [trigger, setTrigger] = useState(false);

  return (
    <div onClick={() => { setTrigger(trigger => !trigger); }}>
      <ChildComponent trigger={trigger}></ChildComponent>
    </div>
  );
};

export const ChildComponent = (props) => {
  const triggerInvokedFromParent = () => {
    console.log('TriggerInvokedFromParent');
  };

  useEffect(() => {
    triggerInvokedFromParent();
  }, [props.trigger]);

  return <span>ChildComponent</span>;
};

В этом примере родительский компонент ParentComponent использует состояние trigger, которое переключается при клике на div. Это состояние передается в дочерний компонент ChildComponent. Внутри дочернего компонента, useEffect следит за изменениями props.trigger и вызывает функцию triggerInvokedFromParent() каждый раз, когда trigger обновляется. Это позволяет вам реагировать на изменения состояния в родительском компоненте.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь