I'm trying to call a parent function with arguments from a child component but I'm not sure exactly how to get it working. I specifically need to be able to call the parent function from the child within another function, so I tried passing a reference to the function through props but this is not quite right. The parent class owns a resource that only it should interact with through the specific function call I'm passing. When done in the following way, I am told the function isn't defined.
export class ParentClass extends React.Component {
ParentFunctionWithArguments(a, b) {
alert("a is being used by my private resource");
alert("b is being used by my private resource");
return true; //some result based on a and b
}
render() {
return (
<ChildClass>ParentFunctionWithArguments={() => this.ParentFunctionWithArguments()}</ChildClass>
);
}
}
And
export class ChildClass extends React.Component {
...
handleOk = (e) => {
...
if (condition) {
if (this.props.ParentFunctionWithArguments(a, b)) {}
}
...
};
...
}
<childComponent callFromChild={this.parentMethod}/>
//in child component call like below
this.props.callFromChild()
You simply need to the apss the parent function as props to the Child component and not call it within the children
export class ParentClass extends React.Component {
ParentFunctionWithArguments(a, b) {
alert("a is being used by my private resource");
alert("b is being used by my private resource");
return true; //some result based on a and b
}
render() {
return (
<ChildClass ParentFunctionWithArguments={this.ParentFunctionWithArguments}></ChildClass>
);
}
}
and simply call it in child like
export class ChildClass extends React.Component {
handleOk = (e) => {
...
if (condition) {
if (this.props.ParentFunctionWithArguments(a, b)) {}
}
...
};
...
}
What I was looking for was the "bind" function
something like this in the constructor of the parent:
export class ParentClass extends React.component {
constructor() {
this.ParentFunctionWithArguments = this.ParentFunctionWithArguments.bind(this);
}
... //rest unchanged
}
This will allow the child to use the passed in parent function in vanilla react.
Related
I have a custom React component, Foo, which contains another custom component, SubFoo, which in turn contains an inline library component, <Bar/>.From Foo I need to access a function, BarFunc, which is returned by <Bar/>. Unfortunately, <Bar/> is not set up to work with refs - when I try to pass in a ref to <Bar/> (forwarded from Foo to SubFoo using React.forwardRef()), I get undefined.
However, there is a div that is parent to <Bar/> in SubFoo, and if I pass my ref to this div I get back an HTMLDivElement.
Is there any way to execute BarFunc given my HTMLDivElement?
There are multiple ways to accomplish what you are looking to do:
Modify SubFoo to proxy the behavior that Bar provides via its render-prop. Then Foo can take a reference to SubFoo and invoke the behavior indirectly:
class SubFoo extends React.Component {
barBehavior = () => this.barBehaviorFunc?.()
renderProp = barApi => {
this.barBehaviorFunc = barApi.barBehavior;
return <div><other components={here} /></div>;
}
render() {
return <Bar>{this.renderProp}</Bar>;
}
}
class Foo extends React.Component {
onClick() { this.ref?.barBehavior();
render() {
return <SubFoo ref={r => this.ref = r} />
}
}
Use the React.Context API to expose this behavior upwards (if you have unmodifiable code in between Foo and SubFoo).
const BarBehaviorContext = React.createContext(() => {});
class Foo extends React.Component {
setBarBehavior = barBehavior => this.barBehavior = barBehavior;
render() { return <BarBehaviorContext.Provider value={this.setBarBehavior}><SubFoo /></BarBehaviorContext.Provider>;
}
class SubFoo extends React.Component {
render() {
return <BarBehaviorContext.Consumer>{setter => <Bar>{api => (setter(api.barBehavior), <div>...</div>)}</Bar>}</BarBehaviorContext.Consumer>
}
}
I have a class extending a custom component which extends another custom component. I want to override handleReload() function to add some extra functionality. I don't want to completely change it's behavior. The problem is that I get errors even when the page loads, and it lacks any functionality.
To sum it up a little bit, there are 3 layers of components, parent -> child -> grandchild, handleReload() is declared in child.
export default class Child extends Parent {
handleReload() {
return () => {
const { name, load } = this.props
load(name)
}
}
<ReloadButton
value={'action.reload'}
onClick={::this.handleReload()}
privilege={`${metaForm(name)}:Read`}
/>
}
Now what I am trying to do is something like this:
export class GrandChild extends Child {
handleReload() {
super.handleReload()
// something else here
}
}
You would need to evaluate something in handleReload to tell it whether it's coming from a grandChild or not and then use a if statement of some sort to do this or that.
// ----- parent.js
export class ParentContainer extends Component {
import {GrandChild} from './components/grandchild';
handleReload = type => {
if(type === 'grandchild'){
// something
} else {
// something else
}
}
render() {
return (
<GrandChild onReload={this.handleReload} />
)
}
}
Example of passing it back in props
// / ----- /components/grandchild.js
export class GrandChild extends Component {
render() {
return (
<button onClick={this.props.onReload('grandchild')}
);
}
}
I have a React Component
export function sendAlert(msg) {
showAlert() //How to call in normal function a function from React.Component
}
export class Alert extends React.Component {
showAlert = () => {
alert("a")
};
render() {
return (
<div>
<h1>Component</h1>
</div>
)
}
}
Its possible to call function which call a function from React.Component
Technically, you can do it, but you should probably extract the common function and call it from both places.
You can do it by creating an instance of the component:
export function sendAlert(msg) {
const alert = new Alert();
alert.showAlert()
}
however, you should extract the code:
function commonShowAlert() {
alert("a");
}
export function sendAlert(msg) {
commonShowAlert();
}
export class Alert extends React.Component {
showAlert = () => {
commonShowAlert();
};
...
...
}
You can do that only if your method is a static one.
You declare your method inside the class as follows :
static showAlert() { alert("a"); }
and then you can access it outside as CLASSNAME.METHOD_NAME
for example : Alert.showAlert();
As #Davin said in his answer, you can declare it outside (that's also my suggestion, cleaner code), but if you have to declare it inside the class, you have the static option.
If your method will not using this, you can use static method
import Alert from '../somewhere';
export function sendAlert(msg) {
Alert.showAlert() // <<--- use Alert.method() to call a static method
}
-
export class Alert extends React.Component {
// Make the method to static
static showAlert = () => {
alert("a")
};
render() {
return (
<div>
<h1>Component</h1>
</div>
)
}
}
Let's say I have the following React Component class:
class SayHello extends React.Component {
constructor(props) {
super(props);
this.handleOnClick = this.handleOnClick.bind(this);
}
render() {
return <div onClick={this.handleOnClick}>Click Me</div>;
}
handleOnClick() {
console.log("clicked");
}
}
What I want to do is create a higher order component that knows about the handleOnClick in SayHello but before calling SayHello's handleOnClick, I want it to execute some code I pass in first (i.e. I want to run code that logs something in my server).
Is there a React pattern for doing something like this?
EDIT:
I want to provide some more context here. I want my higher order component to be dynamic in terms of which methods to call. For example, sometimes it might be handleOnClick but other times it might be handleOnSomethingElse.
A higher-order component is a function that takes a component argument and returns a new component.
This function returns a component with a decorated handleClick method:
// A higher-order component that runs some code before
// the given component's `handleClick` method
function wrapHello(componentClass) {
return class wrapped extends componentClass {
beforeHandleClick() {
console.log("I run first!")
}
handleClick(...args) {
this.beforeHandleClick()
super.handleClick(...args)
}
}
}
This pattern is neat because it isn't specific to React at all; it's just a pure function. That means it's easy to test and reason about.
Here's a test harness that doesn't use React:
function wrapHello(componentClass) {
return class wrapped extends componentClass {
beforeHandleClick() {
console.log("I run first!")
}
handleClick(...args) {
this.beforeHandleClick()
super.handleClick(...args)
}
}
}
class SayHello {
handleClick() {
console.log("handleClick")
}
}
const WrappedHello = wrapHello(SayHello)
new WrappedHello().handleClick()
You need something like a dynamic mixin.
This higher-order component takes a Component class and an Object of decorator methods.
The HOC wraps each method that has a matching decorator. These methods call the decorator then call through to the original component method. Non-decorated methods are unchanged.
// Higher-order component
function decorateMethods(componentClass, decorators) {
class decoratedClass extends componentClass { }
Object.keys(decorators).forEach(decoratorName => {
decoratedClass.prototype[decoratorName] = function(...args) {
decorators[decoratorName].call(this, ...args);
return componentClass.prototype[decoratorName].call(this, ...args)
}
})
return decoratedClass
}
//
// Test
//
class Component {
foo() {
console.log("foo")
}
bar() {
console.log("bar")
}
baz() {
console.log("baz")
}
}
const DecoratedComponent = decorateMethods(Component, {
foo() {
console.log("before foo")
},
bar() {
console.log("before bar")
}
})
const d = new DecoratedComponent()
d.foo()
d.bar()
d.baz()
In this case the decorator methods exactly match the base class method names. If you want the decorator to use, e.g. beforeFoo instead, you could map method names with:
const methodName = decoratorName replace(/before(\w)/, (_, a) => a.toLowerCase())
I'm trying to follow the no-bind rule for React using the pattern that they have recommended with ES6 classes:
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this);
}
render() {
return (
<div onClick={this._onClick}>
Hello!
</div>
);
}
_onClick() {
// Do whatever you like, referencing "this" as appropriate
}
}
However, when I need to pass arguments in to _onClick, what needs to change?
I've tried something like:
import {someFunc} from 'some/path';
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this, a, b);
}
render() {
const {
prop1,
prop2
} = this.props;
return (
<div onClick={this._onClick(prop1, prop2}>
Hello!
</div>
);
}
_onClick = (a, b) => {
someFunc(a, b);
}
}
However, this does not work. What needs to be altered?
The call to bind in the constructor should only pass this as a single argument.
this._onClick = this._onClick.bind(this);
Here you are overwriting the property this._onClick with a new one that has the correct this bound. If your function takes two arguments, then you should pass those as normal at call time.
Passing additional arguments to bind means that the function returned already has those arguments supplied - in your attempt the _onClick function will always have its first two arguments undefined, as a and b have no value in the constructor.
Now that you have bound this to your function, you can access this.props from within there, rather than having to pass arguments:
import {someFunc} from 'some/path';
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this);
}
render() {
return (
<div onClick={this._onClick}>
Hello!
</div>
);
}
_onClick() {
const {
prop1,
prop2
} = this.props;
someFunc(prop1, prop2);
}
}
You should use partial application. Basically you initialise your onClick function with the parameters you want, and the onClick function will return a new function to be called when the div is clicked.
import {someFunc} from 'some/path';
class Foo extends React.Component {
render() {
return (
<div onClick={this._onClick(prop1, prop2)}>
Hello!
</div>
);
}
_onClick = (a, b) => {
return () => someFunc(a, b);
}
}
PS: this only applies if your parameters a and b are not part of your this.props, if they are then you should just do as Tom Fenech said.
To answer your question there is nothing special you have to do in order to pass arguments to your this._onClick function.
the proper revised code will be:
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this);
}
render() {
return (
<div onClick={() => this._onClick(1, 2)}>
Hello!
</div>
);
}
_onClick = (a, b) => {
console.log(a, b);
}
}
Secondly, the way you are calling this._onClick is not the right way to invoke a function on click.
Right now what is happening that on each render process your function is getting called because you didn't pass the function as an argument rather you invoked that function and assigned its returned value to the onClick prop.
you have to do this like:
render() {
return (
<div onClick={() => this._onClick(prop1, prop2)}>
Hello!
</div>
);
}
By Invoking your function this way you ensure the this._onClick will get called when click event occurs.
Another method is to use Babel stage 1 autobind and skip the constructor.
import {someFunc} from 'some/path';
class Foo extends React.Component {
_onClick = () => {
const {
prop1,
prop2
} = this.props;
someFunc(prop1, prop2);
}
render() {
return (
<div onClick={this._onClick}>
Hello!
</div>
);
}
}