React: How to access the props object outside its class? - reactjs

I am not sure how to access the props object outside the context of a receiving child, is there any way? Here is a minimal test file (Jest and Enzyme) I setup to experiment with. I get a syntax error where I try to pass this.props.propNumber in:"this is a reserved word"
const React = require("react");
const enzyme = require("enzyme");
const Adapter = require("enzyme-adapter-react-16");
enzyme.configure({ adapter : new Adapter() });
// simple component with an increment method
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
testIncrement : 1
};
}
handleIncrement = (incrementSize) => {
this.setState({
testIncrement : this.state.testIncrement += incrementSize
})
};
render() {
return (
<div>
Test
</div>
);
}
}
const wrapper = enzyme.shallow(
<Foo
propNumber={5}
/>
);
test('prop passage into shallow instance', () => {
wrapper.instance().handleIncrement(this.props.propNumber);
expect(wrapper.state('testIncrement')).toEqual(6);
});

You know what the prop is because you are passing it in, why do you need to access it from the class?
const incrementSize = 1;
const propNumber = 5;
const wrapper = enzyme.shallow(
<Foo
propNumber={propNumber}
/>
);
test('prop passage into shallow instance', () => {
wrapper.instance().handleIncrement(incrementSize);
expect(wrapper.state('testIncrement')).toEqual(propNumber + incrementSize);
});
And your handleIncrement function doesn't take an array, so no reason to pass it one.

In your parent component file, export the value, and then in your child component file, import the value

Related

ReactJS: TypeError: this.ref.current.method is not a function with ant design form

class Parent extends Component {
constructor(props) {
super(props);
this.Child_A = React.createRef();
this.Child_B = React.createRef();
}
function_uses_Child_A = ()=> {
// This is working fine
this.Child_A.current.Child_A_Function()
}
function_uses_Child_B = ()=> {
// This is Does NOT work
// this.Child_A.current.Child_B_Function() is not a function
this.Child_A.current.Child_B_Function()
}
render() {
return (
<div>
<Child_A ref={this.Child_A}/>
<Child_B ref={this.Child_B}/>
</div>
);
}
}
export default Parent;
The above code shows my problem where both has the same code but one works and the other doesn't
This is Child A component:
class Child_A extends Component {
Child_A_Function = () => "Working";
render = () => <h1>Child_A</h1>
}
export default Child_A;
This is Child B component:
import {Form} from "antd";
class Child_B extends Component {
Child_B_Function = () => "Not Working";
render = () => <h1>Child_B</h1>
}
export default Form.create()(Child_B);
I tried to debug this.Child_B.current
image debug info
I believe it shows the Form.create() data and removing mine
I understand this because Child_A works fine and the only different is it doesn't have Form.create()
This is because Form.create()() is a higher order function which returns another component.
so
const DecoratedChild_B = Form.create()(Child_B);
DecoratedChild_B may have other wrapper around it, and it become like this:
<wrapper ref={this.Child_B}>
<Child_B/>
</wrapper>
That's why you don't get what you want.
to get form ref you should use wrappedComponentRef
const EnhancedForm = createForm()(Form);
<EnhancedForm wrappedComponentRef={(inst) => this.formRef = inst} />
this.formRef // => The instance of Form
if you want something custom, you have to use other name for the ref func

Call child method from parent class - React typescript

How do I call a child method from parent class ? or to put it simply how to assign a ref to ReactElement?
I've seen examples where ref is assigned to HTMLDivElement or HTMLInputElement, but not a ReactElement.
class Example extends React.Component<Props, State> {
...
childRef = React.createRef<React.ReactElement>();
...
next = () => {
this.childRef.someFunction();
}
render() {
<Child ref={this.childRef}/>
}
}
The above code gives me two errors:
Generic type 'ReactElement<P>' requires 1 type argument(s).
Property 'someFunction' does not exist on type 'RefObject<any>'.
The main issue is React.createRef<React.ReactElement>(). You need to change ReactElement to the type that you want, in this case Child.
One more issue in this.childRef.someFunction();. It's missing .current. then it'll be this.childRef.current.someFunction();.
Here's a full example:
Or try live demo on CodeSandbox
import * as React from "react";
import { render } from "react-dom";
interface ChildState {
lastCalled?: Date
}
class Child extends React.Component<{}, ChildState> {
state: ChildState = {};
render() {
if (!this.state.lastCalled) {
return "Not called yet";
}
return `Last called at ${this.state.lastCalled.toLocaleTimeString()}`;
}
someFunction = () => {
this.setState({
lastCalled: new Date()
});
};
}
class App extends React.Component {
childRef = React.createRef<Child>();
next = () => {
if (!this.childRef.current) {
return;
}
this.childRef.current.someFunction();
};
render() {
return (
<React.Fragment>
<Child ref={this.childRef} />
<div>
<button type="button" onClick={this.next}>
Next Call
</button>
</div>
</React.Fragment>
);
}
}
render(<App />, document.getElementById("root"));
Update -- 16 May 2019:
When I opened the CodeSandBox sample above and updated to latest dependencies, it didn't seem to like:
childRef = React.createRef<Child>();
It throws an error on the close bracket ).
To make it work, I changed it to:
childRef:React.RefObject<Child> = React.createRef();
When you use React.createRef(), the resulting object looks like {current : null}. React then assigns whatever the actual reference is to refObject.current.
So, in your example, you need this.childRef.current.someFunction().
You may also have to do some TypeScript declarations to let it know the object stored inside the ref has that function available.
I think you need to pass in a function to assign the reference variable.
private childRef: any;
private assignRef = (ref) => this.childRef = ref;
next = () => {
this.childRef.someFunction();
}
render() {
<Child ref={this.assignRef}/>
}

Checking props of a child component with shallow rendering using enzyme

I have a problem understanding shallow rendering of enzyme.
I have a component WeatherApplication which has a child component CitySelection.
The CitySelection receives a property selectedCity which is hold in the WeatherApplications state.
The component:
export default class WeatherApplication extends React.Component {
constructor(props) {
super(props);
this.state = {
city : "Hamburg"
}
}
selectCity(value) {
this.setState({
city: value
});
}
render() {
return (
<div>
<CitySelection selectCity={this.selectCity.bind(this)} selectedCity={this.state.city} />
</div>
);
}
}
I tested sussessfully that the CitySeleciton exists and that the selectedCity is "Hamburg" and the correct function is passed.
Now I want to test the behaviour of the selectCity method.
it("updates the temperature when the city is changed", () => {
var wrapper = shallow(<WeatherApplication/>);
wrapper.instance().selectCity("Bremen");
var citySelection = wrapper.find(CitySelection);
expect(citySelection.props().selectedCity).toEqual("Bremen");
});
This test fails, because the value of citySelection.props().selectedCity is still Hamburg.
I checked that the render method of WeatherApplication is called again and this.state.city has the correct value. But I cannot fetch it via the props.
Calling wrapper.update() after selectCity() should do the trick:
it("updates the temperature when the city is changed", () => {
var wrapper = shallow(<WeatherApplication/>);
wrapper.instance().selectCity("Bremen");
wrapper.update();
var citySelection = wrapper.find(CitySelection);
expect(citySelection.props().selectedCity).toEqual("Bremen");
});

Testing input.focus() in Enzyme

How do I test input.focus() in enzyme. I am writing the script with react. My code is below:
public inputBox: any;
componentDidUpdate = () => {
setTimeout(() => {
this.inputBox.focus();
}, 200);
}
render() {
return (
<div>
<input
type = 'number'
ref = {element => this.inputBox = element } />
</div>
);
}
You can use mount instead of shallow.
Then you can compare document.activeElement and the input DOM node for equality.
const output = mount(<MyFocusingComponent/>);
assert(output.find('input').node === document.activeElement);
See https://github.com/airbnb/enzyme/issues/316 for more details.
Per React 16.3 updates... using createRef for anyone visiting this post today, if you rearrange the original component to use the new ref api
class InputBox extends PureComponent {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
<input
ref={this.inputRef}
/>
);
}
}
Then in your test spec
it("Gives immediate focus on to name field on load", () => {
const wrapper = mount(<InputBox />);
const { inputRef } = wrapper.instance();
jest.spyOn(inputRef.current, "focus");
wrapper.instance().componentDidMount();
expect(inputRef.current.focus).toHaveBeenCalledTimes(1);
});
Notice the use of the inputRef.current attribute which references the currently assigned DOM node.
Other approach is to test if element gains focus, i.e. focus() is called on node element. To achieve this, focused element need to be referenced via ref tag like it takes place in your example – reference was assigned to this.inputBox. Consider example below:
const wrapper = mount(<FocusingInput />);
const element = wrapper.instance().inputBox; // This is your input ref
spyOn(element, 'focus');
wrapper.simulate('mouseEnter', eventStub());
setTimeout(() => expect(element.focus).toHaveBeenCalled(), 250);
This example uses Jasmine's spyOn, though you can use any spy you like.
I just had the same issue and solved using the following approach:
My setup is Jest (react-create-app) + Enzyme:
it('should set the focus after render', () => {
// If you don't create this element you can not access the
// document.activeElement or simply returns <body/>
document.body.innerHTML = '<div></div>'
// You have to tell Enzyme to attach the component to this
// newly created element
wrapper = mount(<MyTextFieldComponent />, {
attachTo: document.getElementsByName('div')[0]
})
// In my case was easy to compare using id
// than using the whole element
expect(wrapper.find('input').props().id).toEqual(
document.activeElement.id
)
})
This worked for me when using mount and useRef hook:
expect(wrapper.find('input').get(0).ref.current).toEqual(document.activeElement)
Focus on the particular element can be checked using selectors.
const wrapper = mount(<MyComponent />);
const input = wrapper.find('input');
expect(input.is(':focus')).toBe(true);
Selecting by data-test attribute or something similar was the most straight forward solution I could come up with.
import React, { Component } from 'react'
import { mount } from 'enzyme'
class MyComponent extends Component {
componentDidMount() {
if (this.inputRef) {
this.inputRef.focus()
}
}
render() {
return (
<input data-test="my-data-test" ref={input => { this.inputRef = input } } />
)
}
}
it('should set focus on mount', () => {
mount(<MyComponent />)
expect(document.activeElement.dataset.test).toBe('my-data-test')
})
This should work
const wrapper = mount(<MyComponent />);
const input = wrapper.find('input');
expect(input).toHaveFocus();

Call a React component method from outside

I want to call a method exposed by a React component from the instance of a React Element.
For example, in this jsfiddle. I want to call the alertMessage method from the HelloElement reference.
Is there a way to achieve this without having to write additional wrappers?
Edit (copied code from JSFiddle)
<div id="container"></div>
<button onclick="onButtonClick()">Click me!</button>
var onButtonClick = function () {
//call alertMessage method from the reference of a React Element! Something like HelloElement.alertMessage()
console.log("clicked!");
}
var Hello = React.createClass({displayName: 'Hello',
alertMessage: function() {
alert(this.props.name);
},
render: function() {
return React.createElement("div", null, "Hello ", this.props.name);
}
});
var HelloElement = React.createElement(Hello, {name: "World"});
React.render(
HelloElement,
document.getElementById('container')
);
There are two ways to access an inner function. One, instance-level, like you want, another, static level.
Instance
You need to call the function on the return from React.render. See below.
Static
Take a look at ReactJS Statics. Note, however, that a static function cannot access instance-level data, so this would be undefined.
var onButtonClick = function () {
//call alertMessage method from the reference of a React Element!
HelloRendered.alertMessage();
//call static alertMessage method from the reference of a React Class!
Hello.alertMessage();
console.log("clicked!");
}
var Hello = React.createClass({
displayName: 'Hello',
statics: {
alertMessage: function () {
alert('static message');
}
},
alertMessage: function () {
alert(this.props.name);
},
render: function () {
return React.createElement("div", null, "Hello ", this.props.name);
}
});
var HelloElement = React.createElement(Hello, {
name: "World"
});
var HelloRendered = React.render(HelloElement, document.getElementById('container'));
Then do HelloRendered.alertMessage().
You can do like
import React from 'react';
class Header extends React.Component{
constructor(){
super();
window.helloComponent = this;
}
alertMessage(){
console.log("Called from outside");
}
render(){
return (
<AppBar style={{background:'#000'}}>
Hello
</AppBar>
)
}
}
export default Header;
Now from outside of this component you can called like this below
window.helloComponent.alertMessage();
1. With React hooks - useImperativeHandle + useRef
const MyComponent = ({myRef}) => {
const handleClick = () => alert('hello world')
useImperativeHandle(myRef, () => ({
handleClick
}), [/* dependencies (if any) */])
return (<button onClick={handleClick}>Original Button</button>)
}
MyComponent.defaultProps = {
myRef: {current: {}}
}
const MyParentComponent = () => {
const myRef = React.useRef({})
return (
<>
<MyComponent
myRef={myRef}
/>
<button onClick={myRef.current.handleClick}>
Additional Button
</button>
</>
)
}
2. With only React hook - useRef
const MyComponent = ({myRef}) => {
const handleClick = () => alert('hello world')
myRef.current.handleClick = handleClick
return (<button onClick={handleClick}>Original Button</button>)
}
MyComponent.defaultProps = {
myRef: {current: {}}
}
const MyParentComponent = () => {
const myRef = React.useRef({})
return (
<>
<MyComponent
myRef={myRef}
/>
<button onClick={myRef.current.handleClick}>
Additional Button
</button>
</>
)
}
Good Luck...
I've done something like this:
class Cow extends React.Component {
constructor (props) {
super(props);
this.state = {text: 'hello'};
}
componentDidMount () {
if (this.props.onMounted) {
this.props.onMounted({
say: text => this.say(text)
});
}
}
render () {
return (
<pre>
___________________
< {this.state.text} >
-------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
</pre>
);
}
say (text) {
this.setState({text: text});
}
}
And then somewhere else:
class Pasture extends React.Component {
render () {
return (
<div>
<Cow onMounted={callbacks => this.cowMounted(callbacks)} />
<button onClick={() => this.changeCow()} />
</div>
);
}
cowMounted (callbacks) {
this.cowCallbacks = callbacks;
}
changeCow () {
this.cowCallbacks.say('moo');
}
}
I haven't tested this exact code, but this is along the lines of what I did in a project of mine and it works nicely :). Of course this is a bad example, you should just use props for this, but in my case the sub-component did an API call which I wanted to keep inside that component. In such a case this is a nice solution.
With the render method potentially deprecating the returned value, the recommended approach is now to attach a callback ref to the root element. Like this:
ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));
which we can then access using window.helloComponent, and any of its methods can be accessed with window.helloComponent.METHOD.
Here's a full example:
var onButtonClick = function() {
window.helloComponent.alertMessage();
}
class Hello extends React.Component {
alertMessage() {
alert(this.props.name);
}
render() {
return React.createElement("div", null, "Hello ", this.props.name);
}
};
ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));
<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="container"></div>
<button onclick="onButtonClick()">Click me!</button>
You can just add an onClick handler to the div with the function (onClick is React's own implementation of onClick) and you can access the property within { } curly braces, and your alert message will appear.
In case you wish to define static methods that can be called on the component class - you should use statics. Although:
"Methods defined within this block are static, meaning that you can run them before any component instances are created, and the methods do not have access to the props or state of your components. If you want to check the value of props in a static method, have the caller pass in the props as an argument to the static method." (source)
Some example code:
const Hello = React.createClass({
/*
The statics object allows you to define static methods that can be called on the component class. For example:
*/
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
alertMessage: function() {
alert(this.props.name);
},
render: function () {
return (
<div onClick={this.alertMessage}>
Hello {this.props.name}
</div>
);
}
});
React.render(<Hello name={'aworld'} />, document.body);
Hope this helps you a bit, because i don't know if I understood your question correctly, so correct me if i interpreted it wrong:)
It appears statics are deprecated, and the other methods of exposing some functions with render seem convoluted. Meanwhile, this Stack Overflow answer about debugging React, while seeming hack-y, did the job for me.
If you are in ES6 just use the "static" keyword on your method from your example would be the following: static alertMessage: function() {
...
},
Hope can help anyone out there :)
I use this helper method to render components and return an component instance.
Methods can be called on that instance.
static async renderComponentAt(componentClass, props, parentElementId){
let componentId = props.id;
if(!componentId){
throw Error('Component has no id property. Please include id:"...xyz..." to component properties.');
}
let parentElement = document.getElementById(parentElementId);
return await new Promise((resolve, reject) => {
props.ref = (component)=>{
resolve(component);
};
let element = React.createElement(componentClass, props, null);
ReactDOM.render(element, parentElement);
});
}
class AppProvider extends Component {
constructor() {
super();
window.alertMessage = this.alertMessage.bind(this);
}
alertMessage() {
console.log('Hello World');
}
}
You can call this method from the window by using window.alertMessage().
method 1 using ChildRef:
public childRef: any = React.createRef<Hello>();
public onButtonClick= () => {
console.log(this.childRef.current); // this will have your child reference
}
<Hello ref = { this.childRef }/>
<button onclick="onButtonClick()">Click me!</button>
Method 2: using window register
public onButtonClick= () => {
console.log(window.yourRef); // this will have your child reference
}
<Hello ref = { (ref) => {window.yourRef = ref} }/>`
<button onclick="onButtonClick()">Click me!</button>
With React17 you can use useImperativeHandle hook.
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
In this example, a parent component that renders would be able to call inputRef.current.focus().
Though this question is closed, I would like to share another approach.
Here's what worked for me:
Child Component
Child component accepts a prop, let's call it onExportedMethods, the aim is to return the set of instance methods that this component wants to give to consumers.
The decision of what needs to be exposed is done at constructor level.
Consumer Component
pass method for prop onExportedMethods & in the handler keep copy of the set of methods Child component exposes.
Whenever required, parent component can call the exposed method
Checkout the sample here
For dynamic components I used the getDerivedStateFromProps method with props.
You can create function that update the props of the child component, The getDerivedStateFromProps in the child component will handle the update of the props for you.
For example:
class Parent extends React.Component
{
constructor(props)
{
super(props);
this.state = { selectMachine: '1' };
this.setComponent = null;
}
handleMachineChange = (e) =>{
this.setState({selectMachine: e.target.value})
}
}
class Child extends React.Component
{
state = {
programForm: {
machine_id: '1',
}
}
constructor(props)
{
super(props);
}
static getDerivedStateFromProps(props, state) {
if(props.selectMachine !== state.programForm.machine_id){
//Change in props
return{
programForm: { ...state.programForm, machine_id: props.selectMachine }
};
}
return null; // No change to state
}
}

Resources