React JS - Pass Provider components methods to this.children - reactjs

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

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

Is there a way to test if a React component is within another component of a particular type?

Let's for a second assume we have 3 components.
export class ComponentA extends React.Component<IComponentAProps, IComponentAState>{
constructor(props: Readonly<IComponentAProps>) {
super(props)
}
render() {
return(
<ComponentB />
);
}
}
export class ComponentB extends React.Component<IComponentBProps, IComponentBState>{
constructor(props: Readonly<IComponentBProps>) {
super(props)
}
render() {
return(
<ComponentC />
);
}
}
export class ComponentC extends React.Component<IComponentBProps, IComponentBState>{
constructor(props: Readonly<IComponentBProps>) {
super(props)
}
render() {
return(
<ComponentA />
);
}
}
Now obviously this is going to cause an infinite loop. Is there a way for me to check in ComponentC to see whether or not it is ultimately contained within an instance of ComponentA?
I'm not sure this is what you need, but you can achieve this using a React context.
import React, { createContext, FunctionComponent, useContext, useEffect } from 'react';
export const TopDogContext = createContext<string>('');
// The top dog can never be nested!
export const TopDog: FunctionComponent = ({ children }) => {
const aboveMe = useContext(TopDogContext);
useEffect(() => {
if (aboveMe) {
setTimeout(() => alert('Yo, you can\'t nest me!'));
throw new Error('Yo, you can\'t nest me!');
}
}, [aboveMe]);
return (
<TopDogContext.Provider value={'I\'m the top dog!'}>
{ children }
</TopDogContext.Provider>
)
};
// -------
import { TopDog } from './top-dog';
function App() {
<TopDog>
<div className="App">
[... YOUR APP HERE ...]
{/* Will create an error if uncommented */}
{/* <TopDog /> */}
</div>
</TopDog>
}
Note that you can still have multiple <TopDog>, but they will never have one being the ancestor of another.

Call child function in parent with i18next react

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

Modify react child components state for storybook

I have a react Component that I am trying to add to a storybook. It has child components that have component state which changes the way the component displays. I would like to set that component state to show in my storybook. What is the best way to achieve this ?
class ParentComponent extends PureComponent<ParentComponentProps> {
render() {
return (
<ChildComponent />
)
}
}
class ChildComponent extends PureComponent<ChildComponentProps> {
constructor(props) {
super(props);
this.handleOnBlur = this.handleOnBlur.bind(this);
this.state = {
isValid: true
};
}
handleOnBlur() {
this.setState({
isValid: isInputValid()
});
}
render() {
return (
<TextField
placeholder="eg. 12345"
validationMessage={'not a valid input'}
isInvalid={this.state.isValid}
onBlur={this.handleOnBlur}
/>
)
}
}
And Storybook code looks like this at the moment
import React from 'react';
import { Provider } from 'react-redux';
import ParentComponent from './ParentComponent';
export default { title: 'UpdateChildComponent' };
export const FieldValidationShowing = (state) => {
const { store, updateState } = mockStore;
updateState(state);
return (
<Provider store={store}>
<ParentComponent />
</Provider>
);
};
The above code is a sample of what I am doing.

Access method on React Context API from Children's Method

I am new to React and trying to make context API. I have read some similar question but I can not get a solution.
My context provider file :
import React, { Component } from 'react'
const MyContext = React.createContext();
class ContextProvider extends Component {
constructor(props) {
super(props)
this.state = {
isLogin: false
}
}
handleLogin = () => {
this.setState({
isLogin : true
})
}
render() {
return (
<MyContext.Provider value={{
...this.state,
handleLogin : this.handleLogin
}}>
{this.props.children}
</MyContext.Provider>
);
}
}
const ContextConsumer = MyContext.Consumer;
export {ContextProvider, ContextConsumer};
I need to change the state by accessing handleLogin() in the ContextProvider.js after user successfull login :
import React, { Component } from 'react'
import {ContextConsumer} from "./ContextProvider";
class Login extends Component {
onHandleSubmit = () => {
// on submit login success :
// --- how to call handleLogin() in ContextProvider.js here ? ----
}
render() {
return (
<div> --- not expected here ---- </div>
)
}
}
BTW, sorry for my English.
Assuming your Login component is wrapped by the ContextProvider higher up in the hierarchy, you can access context inside class component by define a static contextType .
For that you need to export context from ContextProvider first like
export {ContextProvider, ContextConsumer, MyContext };
and then use it like
import React, { Component } from 'react'
import {MyContext} from "./ContextProvider";
class Login extends Component {
static contextType = MyContext;
onHandleSubmit = () => {
// on submit login success :
this.context.handleLogin();
}
render() {
return (
<div> {/* render content here */} </div>
)
}
}
However if you are using a version of react between 16.3.0 and 16.6.0, you need to pass on context using render props pattern like
class Login extends Component {
onHandleSubmit = () => {
// on submit login success :
this.props.context.handleLogin();
}
render() {
return (
<div> --- not expected here ---- </div>
)
}
}
export default (props) => (
<ContextConsumer>
{values=> <Login {...props} context={values} />}
</ContextConsumer>
)

Resources