How do I use a parent(class component) to call a method in child(function component)?
This is what I have:
///child component(function)
function Child(props) {
const handleParent = () => { console.log("handling parent call")} ///<-- I want to call thuis.
return(
...
)
}
export default class Parent extends Component{
constructor(props){
super(props)
this.state={
consoletext:"",
}
}
componentDidMount() {
...
}
render(){
return(
<div>
<Child />
</div>
)
}
}
I try to use useRef in parent, but got an error:
cannot use "useRef" in class component.
How can I resolve this?
useRef is a react hook that can only be used inside the functional component.
You can use useImperativeHandle to customize the instance value that is exposed to parent components when using ref.
E.g.
Parent.tsx:
import React from "react";
import Child from "./Child";
export default class Parent extends React.Component {
childRef;
constructor(props) {
super(props);
this.childRef = React.createRef();
}
componentDidMount() {
if (this.childRef && this.childRef.current) {
this.childRef.current.handleParent();
}
}
render() {
return (
<div className="App">
<Child ref={this.childRef} />
</div>
);
}
}
Child.tsx:
import React, { useImperativeHandle } from "react";
const Child = React.forwardRef((props, ref) => {
const handleParent = () => {
console.log("handling parent call");
};
useImperativeHandle(ref, () => ({
handleParent
}));
return <div>child</div>;
});
export default Child;
CodeSandbox
Related
In React can methods be passed to {this.children} in a container consumer model. What I mean to ask is I have a provider component and I need to pass or refer the provider components methods in the child component.
export default class ContainerCompo extends React.Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
methodOne() {
//some code
}
methodTwo() {
//some code
}
render() {
return (
{this.props.children}
}
}
export default class InputComponent extends React.Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
validate() {
ContainerCompo.methodOne(param)
}
render() {
return <InputComponent />
}
// Rendering the components
<ContainerCompo>
<InputComponent containerMethods={methods of ContainerCompo}/>
</ContainerCompo>
I hope my question is clear here, please suggest
First create a react context.
import React, { Component, createContext } from 'react';
// Create's authentication context to be use anywhere in the app
const ContainerContext = createContext();
export default ContainerContext;
Then create a provider for it.
export default class ContainerProvider extends Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
methodOne() {
//some code
}
methodTwo() {
//some code
}
render() {
const { children } = this.props;
return (
<ContainerContext.Provider
value={{
container: {
methodOne: (...params) => this.methodOne(...params),
methodTwo: (...params) => this.methodTwo(...params)
}
}}
>
{children}
</ContainerContext.Provider>
)}}
Wrap your App with the provider.
import ContainerProvider from './ContainerProvider'
<ContainerProvider>
<App />
</ContainerProvider>
Then create a consumer for the context
export default function withContainer(InComponent) {
return function ContainerComponent(props) {
return (
<ContainerContext.Consumer>
{({ container }) => <InComponent {...props} container={container} />}
</ContainerContext.Consumer>
);
};
}
Then import the consumer and user in your components and you will get the methods as props
import withContainer from './ContainerConsumer'
render() {
const { container } = this.props;
return(<div />)
}
export default withContainer(YourComponent);
I used React.createRef() to call child method, like that
import Child from 'child';
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>
);
}
}
Child class like that
export default class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
It works well. But when I want to use i18next to translate child component, I have to add withTranslation() to use HOC.
import { useTranslation } from 'react-i18next';
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
const { t } = this.props;
return <h1>{t('Hello')}</h1>;
}
}
export default withTranslation()(Child);
Then return error: Function components cannot be given refs.
Means cannot use ref in <Child /> tag. Is there any way to call child function after add i18next?
This is a problem since the withTranslation HOC is using a function component. By wrapping your Child component with a HOC you essentially are placing the ref on the withTranslation component (by default).
There are multiple ways to fix this problem, here are the two easiest:
Using withRef: true >= v10.6.0
React-i18n has a built in option to forward the ref to your own component. You can enable this by using the withRef: true option in the HOC definition:
export default withTranslation({ withRef: true })(Child);
Proxy the ref using a named prop
Instead of using <Child ref={this.child} />, choose a different prop to "forward" the ref to the correct component. One problem though, you want the ref to hold the component instance, so you will need to assign the ref manually in the lifecycle methods.
import Child from 'child';
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child innerRef={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
import { useTranslation } from 'react-i18next';
class Child extends Component {
componentDidMount() {
this.props.innerRef.current = this;
}
componentWillUnmount() {
this.props.innerRef.current = null;
}
getAlert() {
alert('getAlert from Child');
}
render() {
const { t } = this.props;
return <h1>{t('Hello')}</h1>;
}
}
export default withTranslation()(Child);
I have a React component that I exported it like this:
class Child extends Component {
getStatus() {
return 'I am child!';
}
render() {
return (<div>Child</div>);
}
}
export default withRouter(Child)
And I have another class that needs the ref of "Child" component like this:
class Parent extends Component {
constructor(props) {
super(props);
this.childRef = createRef();
}
handleLogChildStatus = () => {
if (typeof this.childRef.current.getStatus === 'function') {
console.log(this.childRef.current.getStatus());
} else {
console.log(' Can not access to child component via ref! ');
}
}
render() {
return (
<div>
<Child ref={this.childRef} />
<div onClick={this.handleLogChildStatus}>Log child status</div>
</div>
);
}
}
This sample shows me I can not access to child component ref because it wrapped by withRouter HOC.
My question is How I can access to child ref component when it wrapped by nextjs withRouter>
I think one of below should work
export default withRouter(Child, { withRef: true })
2nd option
this.childComponent = React.createRef();
....
<Child wrappedComponentRef={c => (this.childComponent = c)} />
I am trying to triger child method by another child.
by using their parent component.
i know when working with redux i need to change fields on the store
and the the components is rerendering by the needed props.
but in this case i dont need to save any thing, just to run the first child method each press.
import React from 'react';
import Child1Container from "./Child1Container";
import Child2Container from "./Child1Container";
export default class Parentextends React.Component {
constructor(props){
super(props);
}
RunChild1Method(){
// Run Child1 Method
}
render() {
return (
<div>
<Child1Container />
<Child2Container RunChild1Method={this.RunChild1Method} />
<div>
)
}
}
Chiled2 Container
import { connect } from 'react-redux';
import Child2Component from "./Child2Component";
const mapStateToProps = (state, ownProps) => {
return {
RunChild1Method: ownProps.RunChild1Method
};
};
const Child2Container = connect(mapStateToProps)(Child2Component);
export default Child2Container ;
Child2 Component
import React from 'react';
export default class Child2Component extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<button onclick={() => {this.props.RunChild1Method()}}> Click Me!</ button>
)
}
}
to achieve this use refs: https://reactjs.org/docs/refs-and-the-dom.html
export default class Parent extends React.Component {
RunChild1Method(){
if (!this.child1Ref) return;
this.child1Ref.runMethod();
}
render() {
return (
<div>
<Child1Container ref={(comp) => { this.child1Ref = comp; }} />
<Child2Container RunChild1Method={this.RunChild1Method} />
<div>
)
}
}
However (My opinion) I would strongly recommend not structuring your apps like this. Components should be an encapsulation of functionality and this is an anti-pattern which will cause future headaches.
I have a scenario where I want to create an HOC that detects mouse events (e.g. mouseenter, mouseleave) when they occur on the HOC's WrappedComponent, then pass the WrappedComponent a special prop (e.g. componentIsHovered). I got this working by using a ref callback to get the wrapped component instance, then adding event listeners to the wrapped instance in my HOC.
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
export default (WrappedComponent) => {
return class DetectHover extends Component {
constructor(props) {
super(props)
this.handleMouseEnter = this.handleMouseEnter.bind(this)
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.bindListeners = this.bindListeners.bind(this)
this.state = {componentIsHovered: false}
this.wrappedComponent = null
}
componentWillUnmount() {
if (this.wrappedComponent) {
this.wrappedComponent.removeEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.removeEventListener('mouseleave', this.handleMouseLeave)
}
}
handleMouseEnter() {
this.setState({componentIsHovered: true})
}
handleMouseLeave() {
this.setState({componentIsHovered: false})
}
bindListeners(wrappedComponentInstance) {
console.log('wrappedComponentInstance', wrappedComponentInstance)
if (!wrappedComponentInstance) {
return
}
this.wrappedComponent = ReactDOM.findDOMNode(wrappedComponentInstance)
this.wrappedComponent.addEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.addEventListener('mouseleave', this.handleMouseLeave)
}
render() {
const props = Object.assign({}, this.props, {ref: this.bindListeners})
return (
<WrappedComponent
componentIsHovered={this.state.componentIsHovered}
{...props}
/>
)
}
}
}
The problem is that this only seems to work when WrappedComponent is a class component — with functional components the ref is always null. I would just as soon place the WrappedComponent inside <div></div> tags in my HOC and carry out the event detection on that div wrapper, but the problem is that even plain div tags will style the WrappedComponent as a block element, which doesn’t work in my use case where the HOC should work on inline elements, too. Any suggestions are appreciated!
You can pass the css selector and the specific styles you need to the Higher Order Component like this:
import React, {Component} from 'react';
const Hoverable = (WrappedComponent, wrapperClass = '', hoveredStyle=
{}, unhoveredStyle={}) => {
class HoverableComponent extends Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
}
}
onMouseEnter = () => {
this.setState({hovered: true});
};
onMouseLeave = () => {
this.setState({hovered: false});
};
render() {
return(
<div
className={wrapperClass}
onMouseEnter= { this.onMouseEnter }
onMouseLeave= { this.onMouseLeave }
>
<WrappedComponent
{...this.props}
hovered={this.state.hovered}
/>
</div>
);
}
}
return HoverableComponent;
};
export default Hoverable;
And use Fragment instead of div to wrap your component:
class SomeComponent extends React.Component {
render() {
return(
<Fragment>
<h1>My content</h1>
</Fragment>
)
}
And then wrap it like this
const HoverableSomeComponent = Hoverable(SomeComponent, 'css-selector');