How to reference an element in child component? - javascript

I want to reference an element that is located in a child component. I am using document.getElementById from its parent component, and it works good, but I have heard that it is better practice to use ref. Is this true? If so, how is that done?
Parent (container) component:
import CanvasView from '../view/CanvasView'
export default class CanvasContainer extends Component {
componentDidMount(){
var c = document.getElementById('canvas')
//DO SOMETHING WITH c
}
render(){
return(
<CanvasView />
)
}
}
Child (presentation) component:
export default const CanvasView = () => (
<canvas id="canvas"></canvas>
)

Using React's lifecycle events is a bit messy, querying the DOM for elements is bad practice as well. Also , since the child components have their own lifecycle it's tricky to handle these situations. Furthermore, you can't access child refs as the parent components don't have access to the children refs. You may pass a callback function though, and pass the selected child element as a reference to the parent component.
var CanvasView = React.createClass({
componentDidMount(){
console.log('Child component did mount, do your stuff')
this.props.callback(this.exampleRef)
},
render: function(){
return(<div ref={(ref) => this.exampleRef = ref}>Foo</div>)
}
});
var CanvasContainer = React.createClass({
doSomethingWithChild(child){
console.log('Parent component')
child.style.backgroundColor='red';
},
render: function(){
return(<CanvasView callback={(e) => this.doSomethingWithChild(e)}/>);
}
});
ReactDOM.render(<CanvasContainer/>,document.getElementById('app'));
https://jsfiddle.net/nwwzyttt/2/

Related

How to access a function in a child class component from a stateless parent?

I have a redux-store with objects of initial values. And this store will get updated at a few places within the child component.
I created a stateless functional component as parent
const Parent = () => {
const store = useSelector(state => state);
const getInitState = () => {
depends on store, it will return an object as initial state for child component
}
let initState = getInitState(); //it has to be let instead of const, it could be changed during useEffect
useEffect(() => {
some initialization on mount
}, [])
return ( // return is simplified here
<Child initState={iniState} />
)
}
export default Parent;
I have a class child component something like below
class Child extends Component {
state = {
componentState: this.props.initState
}
....
}
export default Child;
I can't modify the child component It's a very complex component with many sub components which I dont handle.
Now I need to access setState function of child component from parent. Or I need to change the state of child from parent, is there a way to do that?
Yes, I understand a new design should be consider since it's anti-pattern, but I am just wondering if I can do it under current setting.
Thank you all in advance.
==============================================================
Edit: For whoever runs into the same problem, functional component does not support constructor. So I have included a breif correction to the answer.
Define parent as below
import React, { useRef } from "react";
const Parent = () => {
const childRef = useRef(null);
return (
<Child ref={childRef} />
)
}
export default Parent;
Then you are able to use childRef.current to access all function from child component.
The best way is using a react Context , and set state in parent then the child consume state of parent (using react hooks would be so easy than class component)
but in your case as you mentiened (I wonder I can do it under current setting)
you can use react refs :
first put ref prop in your rendered component tag then use it in parent to execute function that's declared inside child
as below :
inside parent component :
const Parent = () => {
.
.
.
constructor() {
//create react ref for our component
this.childComponent = React.createRef();
}
callChildFunction() {
// here using refs you can access function in you child refenrenced component
this.childComponent.cuurent.doSomeUpdateStateStuff(newState);
}
return ( // return is simplified here
<Child ref={this.childComponen} initState={iniState} />
)
...
}
and your child :
class Child extends Component {
state = {
componentState: this.props.initState
}
doSomeUpdateStateStuff(state) {
// stuff updating state callled from parent
}
....
}

React not calling function from another component?

I believe I am trying to accomplish something simple but failing to do so.
React is not calling the 'alertFunc()' from the ChildComponent from another component like I hoped it would.
Here is the ChildComp:
class ChildComp extends React.Component {
constructor(props) {
super(props);
this.state = { };
this.input = React.createRef();
}
alertFunc = () => {
alert('This function is called from the Child Component');
};
handleChange = () => {
this.alertFunc();
};
render() {
return (
<ChildComp onChange={this.handleChange} />
);
}
}
Then I'm trying to call it from a parent compolike:
render(props){
return(
<button onClick={props.alertFunc()}>Next</button>
);
}
And the error I get is:
props.alertFunc is not a function
You can't call an instance function of a child component from a parent component like that, you don't have access to that function from the parent. If you wish both components to have access to it (parent and child) you should share it somehow between them, using context at a more upper-level if the hierarchy is deeply nested, or define it in the parent and pass it to the child via props or use redux. Or if doesn't depend on component state move it out of the component.

ReactJS - How to expose your Component to external events? [duplicate]

I have two components:
Parent component
Child component
I was trying to call Child's method from Parent, I tried this way but couldn't get a result:
class Parent extends Component {
render() {
return (
<Child>
<button onClick={Child.getAlert()}>Click</button>
</Child>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
Is there a way to call Child's method from Parent?
Note: Child and Parent components are in two different files.
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>
Documentation for useImperativeHandle() is here:
useImperativeHandle customizes the instance value that is exposed to parent components when using ref.
Legacy API using Class Components (>= react#16.4)
const { Component } = React;
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>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
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>
Callback Ref API
Callback-style refs are another approach to achieving this, although not quite as common in modern React:
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You can use another pattern here:
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
What it does is to set the parent's clickChild method when child is mounted. In this way when you click the button in parent it will call clickChild which calls child's getAlert.
This also works if your child is wrapped with connect() so you don't need the getWrappedInstance() hack.
Note you can't use onClick={this.clickChild} in parent because when parent is rendered child is not mounted so this.clickChild is not assigned yet. Using onClick={() => this.clickChild()} is fine because when you click the button this.clickChild should already be assigned.
Alternative method with useEffect:
Parent:
const [refresh, doRefresh] = useState(0);
<Button onClick={() => doRefresh(prev => prev + 1)} />
<Children refresh={refresh} />
Children:
useEffect(() => {
performRefresh(); //children function of interest
}, [props.refresh]);
Here I will give you the four possible combinations that can happen:
Class Parent | Hook Child
Hook Parent | Class Child
Hook Parent | Hook Child
Class Parent | Class Child
1. Class Parent | Hook Child
class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))
function childMethod() {
console.log('call me')
}
return (<View><Text> I am a child</Text></View>)
})
2. Hook Parent | Class Child
function Parent(props) {
const myRef = useRef()
return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}
class Child extends React.Component {
childMethod() {
console.log('call me')
}
render() {
return (<View><Text> I am a child</Text></View>)
}
}
3. Hook Parent | Hook Child
function Parent(props) {
const myRef = useRef()
return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))
function childMethod() {
console.log('call me')
}
return (<View><Text> I am a child</Text></View>)
})
4. Class Parent | Class Child
class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}
class Child extends React.Component {
childMethod() {
console.log('call me')
}
render() {
return (<View><Text> I am a child</Text></View>)
}
}
https://facebook.github.io/react/tips/expose-component-functions.html
for more answers ref here Call methods on React children components
By looking into the refs of the "reason" component, you're breaking encapsulation and making it impossible to refactor that component without carefully examining all the places it's used. Because of this, we strongly recommend treating refs as private to a component, much like state.
In general, data should be passed down the tree via props. There are a few exceptions to this (such as calling .focus() or triggering a one-time animation that doesn't really "change" the state) but any time you're exposing a method called "set", props are usually a better choice. Try to make it so that the inner input component worries about its size and appearance so that none of its ancestors do.
I wasn't satisfied with any of the solutions presented here. There is actually a very simple solution that can be done using pure Javascript without relying upon some React functionality other than the basic props object - and it gives you the benefit of communicating in either direction (parent -> child, child -> parent). You need to pass an object from the parent component to the child component. This object is what I refer to as a "bi-directional reference" or biRef for short. Basically, the object contains a reference to methods in the parent that the parent wants to expose. And the child component attaches methods to the object that the parent can call. Something like this:
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
someParentFunction: someParentFunction
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef.someChildFunction = someChildFunction;
return <div>
<Button onClick={onButtonClick} />
</div>;
}
The other advantage to this solution is that you can add a lot more functions in the parent and child while passing them from the parent to the child using only a single property.
An improvement over the code above is to not add the parent and child functions directly to the biRef object but rather to sub members. Parent functions should be added to a member called "parent" while the child functions should be added to a member called "child".
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.child.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
parent: {
someParentFunction: someParentFunction
}
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.parent.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef {
child: {
someChildFunction: someChildFunction
}
}
return <div>
<Button onClick={onButtonClick} />
</div>;
}
By placing parent and child functions into separate members of the biRef object, you 'll have a clean separation between the two and easily see which ones belong to parent or child. It also helps to prevent a child component from accidentally overwriting a parent function if the same function appears in both.
One last thing is that if you note, the parent component creates the biRef object with var whereas the child component accesses it through the props object. It might be tempting to not define the biRef object in the parent and access it from its parent through its own props parameter (which might be the case in a hierarchy of UI elements). This is risky because the child may think a function it is calling on the parent belongs to the parent when it might actually belong to a grandparent. There's nothing wrong with this as long as you are aware of it. Unless you have a reason for supporting some hierarchy beyond a parent/child relationship, it's best to create the biRef in your parent component.
I hope I'm not repeating anything from above but what about passing a callback prop that sets the function in the parent? This works and is pretty easy. (Added code is between the ////'s)
class Parent extends Component {
/////
getAlert = () => {} // initial value for getAlert
setGetAlertMethod = (newMethod) => {
this.getAlert = newMethod;
}
/////
render() {
return (
<Child setGetAlertMethod={this.setGetAlertMethod}>
<button onClick={this.getAlert}>Click</button>
</Child>
);
}
}
class Child extends Component {
/////
componentDidMount() {
this.props.setGetAlertMethod(this.getAlert);
}
/////
getAlert() => {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
you can use ref to call the function of the child component from the parent
Functional Component Solution
in functional component, you have to use useImperativeHandle for getting ref into a child like below
import React, { forwardRef, useRef, useImperativeHandle } from 'react';
export default function ParentFunction() {
const childRef = useRef();
return (
<div className="container">
<div>
Parent Component
</div>
<button
onClick={() => { childRef.current.showAlert() }}
>
Call Function
</button>
<Child ref={childRef}/>
</div>
)
}
const Child = forwardRef((props, ref) => {
useImperativeHandle(
ref,
() => ({
showAlert() {
alert("Child Function Called")
}
}),
)
return (
<div>Child Component</div>
)
})
Class Component Solution
Child.js
import s from './Child.css';
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
export default Child;
Parent.js
class Parent extends Component {
render() {
onClick() {
this.refs.child.getAlert();
}
return (
<div>
<Child ref="child" />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
I'm using useEffect hook to overcome the headache of doing all this so now I pass a variable down to child like this:
import { useEffect, useState } from "react";
export const ParentComponent = () => {
const [trigger, setTrigger] = useState(false);
return (
<div onClick={() => { setTrigger(trigger => !trigger); }}>
<ChildComponent trigger={trigger}></ChildComponent>
</div>
);
};
export const ChildComponent = (props) => {
const triggerInvokedFromParent = () => {
console.log('TriggerInvokedFromParent');
};
useEffect(() => {
triggerInvokedFromParent();
}, [props.trigger]);
return <span>ChildComponent</span>;
};
We can use refs in another way as-
We are going to create a Parent element, it will render a <Child/> component. As you can see, the component that will be rendered, you need to add the ref attribute and provide a name for it.
Then, the triggerChildAlert function, located in the parent class will access the refs property of the this context (when the triggerChildAlert function is triggered will access the child reference and it will has all the functions of the child element).
class Parent extends React.Component {
triggerChildAlert(){
this.refs.child.callChildMethod();
// to get child parent returned value-
// this.value = this.refs.child.callChildMethod();
// alert('Returned value- '+this.value);
}
render() {
return (
<div>
{/* Note that you need to give a value to the ref parameter, in this case child*/}
<Child ref="child" />
<button onClick={this.triggerChildAlert}>Click</button>
</div>
);
}
}
Now, the child component, as theoretically designed previously, will look like:
class Child extends React.Component {
callChildMethod() {
alert('Hello World');
// to return some value
// return this.state.someValue;
}
render() {
return (
<h1>Hello</h1>
);
}
}
Here is the source code-
Hope will help you !
If you are doing this simply because you want the Child to provide a re-usable trait to its parents, then you might consider doing that using render-props instead.
That technique actually turns the structure upside down. The Child now wraps the parent, so I have renamed it to AlertTrait below. I kept the name Parent for continuity, although it is not really a parent now.
// Use it like this:
<AlertTrait renderComponent={Parent}/>
class AlertTrait extends Component {
// You will need to bind this function, if it uses 'this'
doAlert() {
alert('clicked');
}
render() {
return this.props.renderComponent({ doAlert: this.doAlert });
}
}
class Parent extends Component {
render() {
return (
<button onClick={this.props.doAlert}>Click</button>
);
}
}
In this case, the AlertTrait provides one or more traits which it passes down as props to whatever component it was given in its renderComponent prop.
The Parent receives doAlert as a prop, and can call it when needed.
(For clarity, I called the prop renderComponent in the above example. But in the React docs linked above, they just call it render.)
The Trait component can render stuff surrounding the Parent, in its render function, but it does not render anything inside the parent. Actually it could render things inside the Parent, if it passed another prop (e.g. renderChild) to the parent, which the parent could then use during its render method.
This is somewhat different from what the OP asked for, but some people might end up here (like we did) because they wanted to create a reusable trait, and thought that a child component was a good way to do that.
For functional components easiest way is
Parent Component
parent.tsx
import React, { useEffect, useState, useRef } from "react";
import child from "../../child"
const parent: React.FunctionComponent = () => {
const childRef: any = useRef();
}
const onDropDownChange: any = (event): void => {
const target = event.target;
childRef.current.onFilterChange(target.value);
};
return <child ref={childRef} />
export default parent;
Child Component
child.tsx
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle, } from "react";
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
onFilterChange(id) {
console.log("Value from parent", id)
},
}));
})
Child.displayName = "Child";
export default Child;
The logic is simple.
Create a function in parent using child or use ref.
I prefer the creating function in parent using child. There are multiple ways to do it.
When using functional components
In Parent
function Parent(){
const [functionToCall, createFunctionToCall] = useState(()=>()=>{})
return (
<Child createFunctionToCall={createFunctionToCall} />
)
}
In Child
function Child({createFunctionToCall}){
useEffect(()=>{
function theFunctionToCall(){
// do something like setting something
// don't forget to set dependancies properly.
}
createFunctionToCall(()=>theFunctionToCall)
},[createFunctionToCall])
}
This pattern is similar to #brickingup answer. But in this version you can set as many child actions you want.
import { useEffect } from "react";
export const Parent = () => {
const childEvents = { click: () => {} };
return (
<div onClick={() => childEvents.click()}>
<Child events={childEvents}></Child>
</div>
);
};
export const Child = (props) => {
const click = () => {
alert("click from child");
};
useEffect(() => {
if (props.events) {
props.events.click = click;
}
}, []);
return <span>Child Component</span>;
};
We're happy with a custom hook we call useCounterKey. It just sets up a counterKey, or a key that counts up from zero. The function it returns resets the key (i.e. increment). (I believe this is the most idiomatic way in React to reset a component - just bump the key.)
However this hook also works in any situation where you want to send a one-time message to the client to do something. E.g. we use it to focus a control in the child on a certain parent event - it just autofocuses anytime the key is updated. (If more props are needed they could be set prior to resetting the key so they're available when the event happens.)
This method has a bit of a learning curve b/c it's not as straightforward as a typical event handler, but it seems the most idiomatic way to handle this in React that we've found (since keys already function this way). Def open to feedback on this method but it is working well!
// Main helper hook:
export function useCounterKey() {
const [key, setKey] = useState(0);
return [key, () => setKey(prev => prev + 1)] as const;
}
Sample usages:
// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
const [inputLineCounterKey, resetInputLine] = useCounterKey();
return <>
<InputLine key={inputLineCounterKey} />
<button onClick={() => resetInputLine()} />
<>;
}
// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
const [amountFocusCounterKey, focusAmountInput] = useCounterKey();
// ... call focusAmountInput in some hook or event handler as needed
return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}
function WorkoutAmountInput(props) {
useEffect(() => {
if (counterKey > 0) {
// Don't focus initially
focusAmount();
}
}, [counterKey]);
// ...
}
(Credit to Kent Dodds for the counterKey concept.)
Parent component
import Child from './Child'
export default function Parent(props) {
const [childRefreshFunction, setChildRefreshFunction] = useState(null);
return (
<div>
<button type="button" onClick={() => {
childRefreshFunction();
}}>Refresh child</button>
<Child setRefreshFunction={(f) => {
setChildRefreshFunction(f);
}} />
</div>
)
}
Child component
export default function Child(props) {
useEffect(() => {
props.setRefreshFunction(() => refreshMe);
}, []);
function refreshMe() {
fetch('http://example.com/data.json')....
};
return (
<div>
child
</div>
)
}
You can achieve this easily in this way
Steps-
Create a boolean variable in the state in the parent class. Update this when you want to call a function.
Create a prop variable and assign the boolean variable.
From the child component access that variable using props and execute the method you want by having an if condition.
class Child extends Component {
Method=()=>{
--Your method body--
}
render() {
return (
//check whether the variable has been updated or not
if(this.props.updateMethod){
this.Method();
}
)
}
}
class Parent extends Component {
constructor(){
this.state={
callMethod:false
}
}
render() {
return (
//update state according to your requirement
this.setState({
callMethod:true
}}
<Child updateMethod={this.state.callMethod}></Child>
);
}
}
Another way of triggering a child function from parent is to make use of the componentDidUpdate function in child Component. I pass a prop triggerChildFunc from Parent to Child, which initially is null. The value changes to a function when the button is clicked and Child notice that change in componentDidUpdate and calls its own internal function.
Since prop triggerChildFunc changes to a function, we also get a callback to the Parent. If Parent don't need to know when the function is called the value triggerChildFunc could for example change from null to true instead.
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
state = {
triggerFunc: null
}
render() {
return (
<div>
<Child triggerChildFunc={this.state.triggerFunc} />
<button onClick={() => {
this.setState({ triggerFunc: () => alert('Callback in parent')})
}}>Click
</button>
</div>
);
}
}
class Child extends Component {
componentDidUpdate(prevProps) {
if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) {
this.onParentTrigger();
}
}
onParentTrigger() {
alert('parent triggered me');
// Let's call the passed variable from parent if it's a function
if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') {
this.props.triggerChildFunc();
}
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>
CodePen: https://codepen.io/calsal/pen/NWPxbJv?editors=1010
Here my demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css
I am using useEffect to call the children component's methods. I have tried with Proxy and Setter_Getter but sor far useEffect seems to be the more convenient way to call a child method from parent. To use Proxy and Setter_Getter it seems there is some subtlety to overcome first, because the element firstly rendered is an objectLike's element through the ref.current return => <div/>'s specificity.
Concerning useEffect, you can also leverage on this approach to set the parent's state depending on what you want to do with the children.
In the demo's link I have provided, you will find my full ReactJS' code with my draftwork inside's so you can appreciate the workflow of my solution.
Here I am providing you my ReactJS' snippet with the relevant code only. :
import React, {
Component,
createRef,
forwardRef,
useState,
useEffect
} from "react";
{...}
// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
// I am fetching the parent's method here
// that allows me to connect the parent and the child's components
let { validateChildren } = props;
// I am initializing the state of the children
// good if we can even leverage on the functional children's state
let initialState = {
one: "hello world",
two: () => {
console.log("I am accessing child method from parent :].");
return "child method achieve";
}
};
// useState initialization
const [componentState, setComponentState] = useState(initialState);
// useEffect will allow me to communicate with the parent
// through a lifecycle data flow
useEffect(() => {
ref.current = { componentState };
validateChildren(ref.current.componentState.two);
});
{...}
});
{...}
// Parent component
class App extends Component {
// initialize the ref inside the constructor element
constructor(props) {
super(props);
this.childRef = createRef();
}
// I am implementing a parent's method
// in child useEffect's method
validateChildren = childrenMethod => {
// access children method from parent
childrenMethod();
// or signaling children is ready
console.log("children active");
};
{...}
render(){
return (
{
// I am referencing the children
// also I am implementing the parent logic connector's function
// in the child, here => this.validateChildren's function
}
<Child ref={this.childRef} validateChildren={this.validateChildren} />
</div>
)
}
You can apply that logic very easily using your child component as a react custom hook.
How to implement it?
Your child returns a function.
Your child returns a JSON: {function, HTML, or other values} as the example.
In the example doesn't make sense to apply this logic but it is easy to see:
const {useState} = React;
//Parent
const Parent = () => {
//custome hook
const child = useChild();
return (
<div>
{child.display}
<button onClick={child.alert}>
Parent call child
</button>
{child.btn}
</div>
);
};
//Child
const useChild = () => {
const [clickCount, setClick] = React.useState(0);
{/* child button*/}
const btn = (
<button
onClick={() => {
setClick(clickCount + 1);
}}
>
Click me
</button>
);
return {
btn: btn,
//function called from parent
alert: () => {
alert("You clicked " + clickCount + " times");
},
display: <h1>{clickCount}</h1>
};
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I tried using createRef or useRef. Somehow they all return null.
Secondly, this answer proposes to pass a prop that sets a function that seems the most reasonable to me. But if your child component is used in multiple places, you should add that extra prop to other places. Also if you want to call a method in the grandchild, this method might be too verbose or mouthful.
So I made my own function store in a very primitive way.
Below is functionStore.js file
const fns = {};
export function setFn(componentName, fnName, fn) {
if (fns[componentName]) {
fns[componentName][fnName] = fn;
} else {
fns[componentName] = { fnName: fn };
}
}
export function callFn(componentName, fnName) {
fns[componentName][fnName]();
}
I just set the functions that need to be called from any component.
import { setFn } from "./functionStore";
export class AComponent extends React.Component {
componentDidMount() {
setFn("AComponent", "aFunc", this.aFunc);
}
aFunc = () => { console.log("aFunc is called!"); };
}
Then I just call it from some other component
import { callFn } from "./functionStore";
export class BComponent extends React.Component {
// just call the function
bFunc = () => {
callFn("AComponent", "aFunc");
};
}
One disadvantage is the function to be called should be parameterless. But this might be fixed somehow as well. Currently, I don't need to pass parameters.
I think that the most basic way to call methods is by setting a request on the child component. Then as soon as the child handles the request, it calls a callback method to reset the request.
The reset mechanism is necessary to be able to send the same request multiple times after each other.
In parent component
In the render method of the parent:
const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);
The parent needs 2 methods, to communicate with its child in 2 directions.
sendRequest() {
const request = { param: "value" };
this.setState({ request });
}
resetRequest() {
const request = null;
this.setState({ request });
}
In child component
The child updates its internal state, copying the request from the props.
constructor(props) {
super(props);
const { request } = props;
this.state = { request };
}
static getDerivedStateFromProps(props, state) {
const { request } = props;
if (request !== state.request ) return { request };
return null;
}
Then finally it handles the request, and sends the reset to the parent:
componentDidMount() {
const { request } = this.state;
// todo handle request.
const { onRequestHandled } = this.props;
if (onRequestHandled != null) onRequestHandled();
}
Here's a bug? to look out for:
I concur with rossipedia's solution using forwardRef, useRef, useImperativeHandle
There's some misinformation online that says refs can only be created from React Class components, but you can indeed use Function Components if you use the aforementioned hooks above. A note, the hooks only worked for me after I changed the file to not use withRouter() when exporting the component. I.e. a change from
export default withRouter(TableConfig);
to instead be
export default TableConfig;
In hindsight the withRouter() is not needed for such a component anyway, but usually it doesn't hurt anything having it in. My use case is that I created a component to create a Table to handle the viewing and editing of config values, and I wanted to be able to tell this Child component to reset it's state values whenever the Parent form's Reset button was hit. UseRef() wouldn't properly get the ref or ref.current (kept on getting null) until I removed withRouter() from the file containing my child component TableConfig

How can I append external DOM to React component?

I have a page with a form rendered in the server, it handles validation, and the correct value for the selects.
I want to hide the DOM of that form, and append it into a react component so I can use it in react-router.
const NewItem = React.createClass({
render() {
return (
<div>
<h1>New item</h1>
{/* I WANT THE FORM FROM THE PAGE HERE*/}
</div>
)
}
})
What is the best way to do it?
You have full access to the DOM in componentDidMount. You can use refs to access the specific DOM element you want.
var NewItem = React.createClass({
componentDidMount: function () {
this.refs.formTarget.appendChild(myFormDomElement);
},
render: function () {
return React.DOM.div(null,
React.DOM.h1(null, "New item"),
React.DOM.div({ref: "formTarget"}));
}
});
Note that in 0.14, a ref is a raw DOM element. Prior to that a ref was a react component and you had to call React.findDOMNode(it) to get the actual DOM element.
React > 16.3
Try using portals like in this component:
import {Component} from 'react';
import {createPortal} from 'react-dom';
import PropTypes from 'prop-types';
class DOMPortal extends Component {
constructor(props) {
super(props);
this.el = document.createElement(props.component);
}
componentDidMount() {
this.props.parentEl.appendChild(this.el);
}
componentWillUnmount() {
this.props.parentEl.removeChild(this.el);
}
render() {
return createPortal(
this.props.children,
this.el,
);
}
}
DOMPortal.propTypes = {
parentEl: PropTypes.object.isRequired,
component: PropTypes.string,
};
DOMPortal.defaultProps = {
component: 'div',
};
Now you can pass your external DOM reference as the parentEl props to it:
<DOMPortal parentEl={decorator.contentWidget.domNode}>...children</DOMPortal>
React < 16.3
Using this.refs is "deprecated", try this instead :
render() {
return <div ref={(DOMNodeRef) => {
this.componentRef=DOMNodeRef;
}}>
...
</div>;
}
Then this.componentRef will be accesible in componentDidMount() so you can append your external DOM element:
componentDidMount(){
this.componentRef.appendChild(externalDOMelement);
}
Notes:
Remember that this.componentRef changes over time (renders()), so you must update it wherever you are passing it to.
Check for a defined reference before using it: if(this.componentRef){// ... your code}
Functional Components' refs are handled differently.
Source:
React Doc
React gives us the functionality dangerouslySetInnerHTML. it gives us the support of adding HTML element. for example
function createMarkup() {
return {__html: 'First ยท Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
You can also use portals introduced in react-16 for appending React component anywhere in the tree by using following code.
ReactDOM.createPortal(child, container)
follow following link Portals for refference

Clone a component that has already been mounted

I am trying to build an app that uses drag-and-drop behaviour, and the component being dragged needs to be cloned elsewhere in the DOM. Since the component is already mounted, trying to mount it again causes the browser to hang.
Trying to use cloneWithProps results in a Cannot read property 'defaultProps' of undefined error.
Here's a testcase:
var TestCase = React.createClass({
getInitialState () {
return {
draggingItem: null
}
},
render () {
return <div>
<ExampleComponent onClick={this.setDraggingItem} />
{this.state.draggingItem}
</div>
},
setDraggingItem (component) {
// This gives `Cannot read property 'defaultProps' of undefined`
//React.addons.cloneWithProps(component)
// This crashes the browser
//this.setState({ draggingItem: component })
}
})
var ExampleComponent = React.createClass({
render () {
return <div onClick={this.handleOnClick}>Hello World</div>
},
handleOnClick (event) {
this.props.onClick(this)
}
})
React.render(<TestCase />, document.body)
Of course I could simply clone component.getDOMNode() in setDraggingItem, but it really seems like rendering the component or calling cloneWithProps should work?
The two things you need to create an element is: the component class (e.g. ExampleComponent) and its props. cloneWithProps is only to be used in render and only with an element coming from props which was created in another component's render. You shouldn't save elements, or pass them around other than to other components in render. Instead, you pass around objects (props) and component classes.
Since you need to know the props and component class to render it in the first place, you can handle all of this in TestCase.
var TestCase = React.createClass({
getInitialState () {
return {
draggingItem: null,
draggingItemProps: null
}
},
render () {
return <div>
<ExampleComponent onClick={this.setDraggingItem.bind(null,
/* the component class */ ExampleComponent,
/* the props to render it with */ null
)} />
{
this.state.draggingItem && React.createElement(
this.state.draggingItem,
this.state.draggingItemProps
)
}
</div>
},
setDraggingItem (component, props, event) {
this.setState({ draggingItem: component, draggingItemProps: props })
}
});
var ExampleComponent = React.createClass({
render () {
return <div onClick={this.handleOnClick}>Hello World</div>
},
// just defer the event
handleOnClick (event) {
this.props.onClick(event)
}
});
If you wish to make these valid outside this TestCase component, ensure there aren't any functions bound to TestCase in the props. Also ensure there's no children prop with react elements in it. If children are relevant, provide the {componentClass,props} structure needed to recreate them.
It's hard to tell what your actual requirements are, but hopefully this is enough to get you started.
You need be sure you're creating a new component with the same props, not mount the same one multiple times. First, setup a function that returns an instantiated components (easier to drop JSX here):
function getComponent(props) {
return ExampleComponent(props);
}
Then in your TestCase render:
return (<div>
{ getComponent({ onClick: this.setDraggingItem }) }
{ this.state.draggingItem }
</div>);
That will create the first component. Then to create a clone:
setDraggingItem(component) {
var clone = getComponent(component.props);
}
This deals with the cloning part. You still have some dragging and rendering to figure out.

Categories