Call Child Method from Parent

Call child method from parent

First off, let me express that this is generally not the way to go about things in React land. Usually what you want to do is pass down functionality to children in props, and pass up notifications from children in events (or better yet: dispatch).

But if you must expose an imperative method on a child component, you can use refs. Remember this is an escape hatch and usually indicates a better design is available.

Previously, refs were only supported for Class-based components.
With the advent of React Hooks, that's no longer the case

Modern React with Hooks (v16.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>

Call child method from parent class Javascript

Is it a good practice to call child method from a parent class?

Yes, this is totally normal. However, your base class should either provide a default implementation for constructHtml, or declare it as abstract (in TypeScript). To be precise, it is only a good practice to call methods declared on the class itself, although they might be (or even expected to be) overridden in a subclass.

the call to this.constructHtml() returns undefined

That's because you have a return; statement doesn't return anything. Remove the linebreak. Don't use Allman brace style in JavaScript.

Call a child method in parent component and change the DOM in the child component

This is because the variable preview is always null. Inside the checkProfilePicture reference function, the if condition will return false, hence it will trigger setIsPreview(true) which doesn't really change the isPreview variable to true. You can try changing that particular setIsPreview argument to false and you should see that it's really working as expected.

Try this example below:

const {
useState,
useRef,
forwardRef,
useImperativeHandle,
} = React;

const Profilepic = forwardRef(function(props, ref) {

const [preview, setPreview] = useState(null);
const [isPreview, setIsPreview] = useState(true);

useImperativeHandle(ref,() => ({
isPreview,
checkProfilPicture() {
console.log("triggered")
if(preview) {
setIsPreview(true);
console.log("Setting isPreview to true");
return false;

}
else {
setIsPreview(false);
console.log("Setting isPreview to false");
return true;

}
}

}));

return (
<strong>{isPreview.toString()}</strong>
)

});

function Input() {
const profilePicRef = useRef();

const checkAll = () => {
const pr = profilePicRef.current.checkProfilPicture();
};

return (
<div>
<Profilepic ref={profilePicRef}></Profilepic>
<button
onClick={checkAll}>
Speichern & Weiter
</button>
</div>
);
}

function App() {
return (
<Input />
);
}

ReactDOM.render(
<App />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Why is Typescript calling an overridden child method from within the parent class if I call child.listen()?

this in a function depends on how it is executed. Here it is executed on child instance so child's setupServer method will be called.

Regarding the question as to why removing the setupServer from base class shows an error in TS:

class Base {

listen() {
this.setupServer();
}
}

class Child extends Base {

setupServer() {
console.log("ChildClass")
}
}

const child = new Child();
child.listen();

It is simple, in this case you can anytime create an object from Base Class and that will fail when you call this.listen(). Typescript cannot let you do that even if setupServer function is defined later in child class.

Call Child Function from Parent in Reactjs using useRef

You've got this almost right, but typeof Child is not giving you an accurate type because the Child component itself is not typed strictly enough. It's getting inferred as forwardRef<unknown, {}>. We need to specificity the type of the forwarded ref in order for this to work.

We can define an interface for the ref that we want:

interface CanShowAlert {
showAlert(): void;
}

We set the two generics on the forwardRef function for Child. CanShowAlert is the ref type and {} is the props type:

const Child = forwardRef<CanShowAlert, {}>((props, ref) => {

We use the same generic on the useRef:

const childRef = useRef<CanShowAlert>(null);

You get an error with your initial value useRef<CanShowAlert>(Child) because the ref is for an instance of the component, but you are passing in the component function itself. Instead we use null as the initial value.

The current property of the ref will either be a component instance or null, so we need to make sure that it's not null before we call the showAlert function. We can use the optional chaining operator ?. for that.

childRef.current?.showAlert()

Complete code:

import React, {forwardRef, useRef, useImperativeHandle} from "react";

interface CanShowAlert {
showAlert(): void;
}

export default function App() {
const childRef = useRef<CanShowAlert>(null);
return (
<div className="container">

<Child ref={childRef} />
<button
onClick={() => { childRef.current?.showAlert() }}
>
Call Function
</button>

</div>
)
}

const Child = forwardRef<CanShowAlert, {}>((props, ref) => {
useImperativeHandle(
ref,
() => ({
showAlert() {
alert("Child Function Called")
console.log('hello world')
}
}),
)
return (
<div>Child Component</div>
)
})

How to enforce mandatory parent method call when calling child method?

Yeah, it's definitely possible, at least during runtime. U can create a Metaclass that modifies how classes are created, the idea is to change the necessary_method signature by adding a mother_method_called flag that is only going to be true when the Mother version is called, otherwise it's going to raise a Value Error. For example in python3:

class MotherMetaClass(type):
def __new__(cls, name, bases, attrs):
method_name = 'necessary_method'
mother_class_name = 'Mother'
original_necessary_method = attrs.get(method_name)

def necessary_method_validated(*args, **kwargs):
original_necessary_method(*args, **kwargs)

if name == mother_class_name:
cls.mother_method_called = True
else:
if not cls.mother_method_called:
raise ValueError(f'Must call "super()" when overriding "{method_name}".')
cls.mother_method_called = False

attrs[method_name] = necessary_method_validated

return super().__new__(cls, name, bases, attrs)

class Mother(metaclass=MotherMetaClass):
def necessary_method(self):
print('Parent method called.')

class GoodChild(Mother):
def necessary_method(self):
super().necessary_method()
print('Good child method called.')

class BadChild(Mother):
def necessary_method(self):
print("Bad child method called.")

a = GoodChild()
a.necessary_method() # it's going to work properly

b = BadChild()
b.necessary_method() # it's going to raise Value Error

C++ How to call a Child Method from Parent

As you've seen, this code won't compile because A doesn't have an f method. In order to make it work, you'd have to explicitly downcast the pointer:

B* tmp = dynamic_cast<B*>(v);
tmp->f();

Can't call child class method from parent class

The way you've written your code, an Inventory is a type of Soldier, which doesn't make sense. Instead, a Soldier should have an Inventory:

class Soldier:
def __init__(self, name, price, health=100, kills=0, soldier_type='human'):

assert price >= 0
assert health >= 0
assert kills >= 0
assert soldier_type in ('human', 'air', 'ground')
self.name = name
self.price = price
self.health = health
self.kills = kills
self.type = soldier_type
self.inventory = Inventory()

@property
def get_name(self):
return self._name

@get_name.setter
def set_name(self, value):
self._name = value

def get_health(self):
return self.health

def set_health(self, value):
self.health = value

def face_damage(self, value):
self.health = self.health - int(value)

def is_alive(self):
if self.health > 0:
return True
else:
return False

def get_inventory(self):
return self.inventory

class Inventory:

weapon_specs = {
'rifle': {'air': 1, 'ground': 2, 'human': 5},
'pistol': {'air': 0, 'ground': 1, 'human': 3},
'rpg': {'air': 5, 'ground': 5, 'human': 3},
'warhead': {'air': 10, 'ground': 10, 'human': 10},
'machine gun': {'air': 3, 'ground': 3, 'human': 10}
}

def __init__(self):
self.inv = set()

def add_weapon(self, item):
# No need to dedupe since we made this a set instead of a list.
self.inv.add(item)

def get_total_attack(self):
damages = {}
for weapon in self.inv:
for k, v in self.weapon_specs[weapon].items():
damages[k] = damages.get(k, 0) + v
return damages

def get_inventory(self):
return self.inv

def attack(self, total):
# not sure what this is actually supposed to do, but this
# is the closest approximation of what your code was TRYING to do?
return total.values()

def test_p2_4():
soldier = Soldier('ranger', 100)
inventory = soldier.get_inventory()
inventory.add_weapon('rifle')
inventory.add_weapon('machine gun')
inventory.add_weapon('rpg')
damages = inventory.get_total_attack()
assert damages['air'] == 9 and damages['ground'] == 10 and damages['human'] == 18
return "Passed!"

print(test_p2_4())


Related Topics



Leave a reply



Submit