Call child function in parent with i18next react - reactjs

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

Related

reactjs - class parent component call function child component method

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

React JS - Pass Provider components methods to this.children

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

How to access component with ref when it wrapped by withRouter in NextJS?

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

Unable to call a property of child react component from parent react component

Following is the jsx code of child & parent parent react components. I am trying to pass the data from child react component to parent react component as property (generateReport) but it throws the error as
Uncaught TypeError: this.props.generateReport is not a function
at MetricsReport.generateReport (metrics-report.jsx:40)
child.jsx
import React, { Component } from 'react';
import {
Row,
Col,
Input,
Collapsible,
CollapsibleItem
} from 'react-materialize';
class MetricsReport extends Component {
constructor(props) {
super(props);
this.state = {
metricsParams: { reportType: '' }
};
this.getReportType = this.getReportType.bind(this);
// Add the below line as per the answer but still facing the problem
this.generateReport = this.generateReport.bind(this);
}
getReportType(event) {
console.log(this.state.metricsParams);
let metricsParams = { ...this.state.metricsParams };
metricsParams.reportType = event.target.value;
this.setState({ metricsParams });
}
generateReport() {
this.props.generateReport(this.state.metricsParams);
}
componentDidMount() {}
render() {
return (
<div class="ushubLeftPanel">
<label>{'Report Type'}</label>
<select
id="metricsDropDown"
className="browser-default"
onChange={this.getReportType}
>
<option value="MetricsByContent">Metrics By Content</option>
<option value="MetricsByUser">Metrics By User</option>
</select>
<button onClick={this.generateReport}>Generate Report</button>
</div>
);
}
}
export default MetricsReport;
parent.jsx
import React, { Component } from 'react';
import MetricsReport from '../components/pages/metrics-report';
class MetricsReportContainer extends Component {
constructor(props) {
super(props);
this.generateReport = this.generateReport.bind(this);
}
generateReport(metricsParams) {
console.log(metricsParams);
}
componentDidMount() {}
render() {
return (
<div>
<MetricsReport generateReport={this.generateReport} />
</div>
);
}
}
export default metricsReportContainer;
You forgot to bind the context this inside child component MetricsReport:
// inside the constructor
this.generateReport = this.generateReport.bind(this);
But you may simply use like this:
<button
onClick={this.props.generateReport(this.state.metricsParams)}
>
Generate Report
</button>
You can handle these scenarios by using anonymous functions instead of normal function. Anonymous function handles your this and removes the need to binding.
generateReport(metricsParams) {
console.log(metricsParams);
}
becomes
generateReport = (metricsParams) => {
console.log(metricsParams);
}
Also in child class
generateReport() {
this.props.generateReport(this.state.metricsParams);
}
becomes
generateReport = () => {
var metricsParams = this.state.metricsParams;
this.props.generateReport(metricsParams);
}

React Higher Order Component that detects dom events that takes functional components as arg

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

Resources