Get HTMLInputElement from ref to PrimeReact's InputText - reactjs

I am trying to use a ref to get to the underlying input element of an InputText component. I used this.textFieldRef = React.createRef() to set up the ref, and then the attribute ref={this.textFieldRef} to hook it up. Yet in the componentDidMount I cannot use this.textFieldRef.current.select() because select() is not a function available for that object. So somehow, InputText is not returning the underlying HTMLInputElement.
Does anyone know how I can get from a ref to something that allows me to select() the text in the InputText element?
Here is my code, which is actually in TypeScript...
import * as React from 'react';
import { InputText } from 'primereact/inputtext';
export class ValueCard extends React.Component<{}, {}> {
textFieldRef: React.RefObject<any> = React.createRef();
componentDidMount = () => {
if (this.textFieldRef.current instanceof InputText) {
this.textFieldRef.current.select();
}
}
render() {
return = (
<InputText
value="test"
ref={this.textFieldRef}
/>
);
}
}

Looking at the source for PrimeReact's InputText component (source), they are attaching a reference to the inner input element to this.element.
This allows you to just add .element to your reference:
this.textFieldRef.current.element.select();
I tested it out in this sandbox, and it seems to work as expect:
https://codesandbox.io/s/203k7vx26j

Maybe you could try to use react-dom library:
ReactDOM.findDOMNode(this.textFieldRef.current).querySelector('input');

Related

React useForm equivalent in class component

I moved a functional component to class component recently and don't find the equivalent of useForm when using a class.
Here is the code I used before:
var commentField = form.getState()["active"].replace(new RegExp("parameter_info$"), "comment");
form.change(commentField, result.comment);
With this, I was able to link two dynamicaly generated inputs.
How can I do it using a class?
FinalForm docs says useForm() is used internally inside useField(), <Field/>, and <FormSpy/>., so it's possible to write equivalent code using FormSpy:
class Updater extends React.Component {
componentWillReceiveProps(nextProps) {
var commentField = this.props.form.getState()["active"].replace(new RegExp("parameter_info$"), "comment");
this.props.form.change(commentField, result.comment);
}
render() {
return null;
}
}
You need to render <FormSpy subscription={{ values: true }} component={Updater} /> somewhere in your form to make it work.
Note that you can use final-form-calculate package to implement calculations between fields.

react native: attempted to assign to readonly property

I create a React ref by calling React.createRef in React Native. Then I assign it to a ref. I got the error: Attempted to assign to readonly property
export default class List extends PureComponent<Props, object> {
private flatListRef: React.RefObject<FlatList<any>>;
constructor(props) {
super(props);
this.flatListRef = React.createRef();
}
render() {
return (
/.../
<FlatList ref={this.flatListRef}></FlatList>
)
}
}
But When I use the callback way to assign the react ref, everything is ok.
<FlatList ref={ele => { this.flatListRef = ele }}></FlatList>
I have no idea what's the difference between the two ways
the ref property in React is expecting a function and it's called immediately after the component is mounted. You can do other things besides setting a reference.
https://zhenyong.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute

Enzyme- How to set state of inner component?

I need to access the state of the inner component, to make it active for click event, my problem is Enzyme does not allow this when using mount, this can only be achieved by shallow rendering of enzyme as mentioned over here, also as mentioned I have tried to use dive to fetch the Form component and again from Form to get Button component which I need to reach, the problem is that my test case keeps on failing as Form component length is zero.
enzyme: 3.1.0
enzyme-adapter-react-15: 1.0.1"
I am pretty new to Enzyme, Any help will be appreciated, Thanks
contactus.test.js :
test('It should simulate submit action ', ()=>{
let contactUs = shallow(<ContactUs />);
sinon.spy(ContactUs.prototype, 'submitMessage');// Verify this method call
let form = contactUs.find(Form)
expect(form.length).toBe(1);//Failing over here
let button = form.dive().find(Button);
expect(button.length).toBe(1);
button.setState({disabled : false});//Need to achieve this
expect(button).toBeDefined();
expect(button.length).toBe(1);
expect(button.props().label).toBe('SEND MESSAGE');
button.find('a').get(0).simulate('click');
expect(ContactUs.prototype.submitMessage).toHaveProperty('callCount',
1);
});
contactus.js :
import React, {Component,PropTypes} from 'react';
import Form from './form';
import {sendSubscriptionMessage} from '../../network';
import Button from '../Fields/Button';
export default class ContactUs extends Component {
constructor(props) {
super(props);
this.state = {
contactData: {}
}
}
onChangeHandler(event) {
let value = event.target.value;
this.state.contactData[event.target.name] = value;
}
submitMessage(event) {
event.preventDefault();
sendSubscriptionMessage(this.state.contactData);
}
render() {
return (<div className = "row pattern-black contact logo-container" id = "contact">
<div className = "container" >
<h2 className = "sectionTitle f-damion c-white mTop100" >
Get in Touch!
<Form onChangeHandler = {
this.onChangeHandler.bind(this)
} >
<Button onClick = {
this.submitMessage.bind(this)
}
className = "gradientButton pink inverse mTop50"
label = "SEND MESSAGE" / >
</Form> </div>
</div>
);
}
}
First of all I think you should not test the Button and the Form functionalities here. In this file you should test only the ContactForm component.
For the first fail, this should work:
find('Form') (the Form should have quotes)
Same for the button:
find('Button');
In this way you don't even have to import the Form and the Button components in your test file at all;
Then, you don't have to set any state for that button. You test the button functionality in the Button.test.js file.
All you have to do here is to call its method like this:
button.nodes[0].props.onClick();
Overall, this is how your test should look like ( note that I didn't test it, I've been using Jest for testing my components, but the logic should be the same ):
test('It should simulate submit action ', ()=>{
const wrapper = shallow(<ContactUs />);
const spy = sinon.spy(ContactUs.prototype, 'submitMessage'); // Save the spy into a new variable
const form = wrapper.find('Form') // I don't know if is the same, but in jest is enough to pass the component name as a string, so you don't have to import it in your test file anymore.
expect(form).to.have.length(1);
const button = wrapper.find('Button'); // from what I see in your code, the Button is part of the ContactUs component, not of the Form.
expect(button).to.have.length(1);
/* These lines should not be part of this test file. Create a test file only for the Button component.
button.setState({disabled : false});
expect(button).toBeDefined();
expect(button.length).toBe(1);
expect(button.props().label).toBe('SEND MESSAGE');
*/
button.nodes[0].props.onClick(); // Here you call its method directly, cause we don't care about its internal functionality; we want to check if the "submitMessage" method has been called.
assert(spy.called); // Here I'm not very sure... I'm a Jest fan and i would have done it like this "expect(spy).toHaveBeenCalled();"
});

Draftjs components with props

I'm new to draftjs and I was wondering if there was a way to render my custom components inline in the editor.
I have a string with twitter handles. I use the decorator to detect regex #[{handle}] which replaces the handle and renders the component inline. However my handle component needs properties such as a callback function and a URL.
I'm not too sure how to pass my component the URL and callback function which I pass into my ContentEditable component.
I'm sure I'm just missing something. I've checked the contentState.getEntity(entityKey).getType() but it only sees the content I pass into the composite decorator as unstyled and not the decorated parts as separate blocks.
I've seen that you can modify the entity map, but I'm not sure if this is the right approach or how to define my own entity in the entity map
Does anyone know what I am missing to give properties to my component?
const decorator = new CompositeDecorator([
{
strategy: handleStrategy,
component: Handle,
},
]);
export default class ContentEditable extends component {
const content = 'some messages and my handle #[handle]';
if (this.props.content.trim() !== '') {
const processedHTML = DraftPasteProcessor.processHTML(content);
const entityMap = processedHTML.entityMap;
const contentState = ContentState.createFromBlockArray(processedHTML.contentBlocks, entityMap);
// Create with content with decorator
editorState = EditorState.createWithContent(contentState, decorator);
} else {
// Create empty content with decorator
editorState = EditorState.createEmpty(decorator);
}
this.state = {
editorState,
}
}
render() {
return (
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
ref="editor"
/>
);
}
I'm sorry the document is missing it. You can provide props in CompositeDecorator like CompositeDecorator({strategy:xxx,component:xxx,props:{...}})
Checking the source

is there any way to access the parent component instance in React?

I know it's not a functional approach to be able to do something like this.parent in a React component, and I can't seem to find any properties on a React component instance that lead to the parent, but I'm just looking to be able to do some custom things where I need this.
Before anyone wastes their time explaining it's not the functional React "way," understand that I need this because of the following I'm trying to achieve:
Build a transpiler for Meteor's Spacebars templating engine, whose rendering model does take into consideration parent components/templates.
I've already built a transpiler that modifies the output jsx to achieve this. I do this by passing in parent={this} in all child components composed. However, after the fact it occurred to me that maybe I simply don't know of something that will give me a way to access the parent component instance without additional transpilation modifications.
Any tips would be much appreciated.
There's nothing wrong if you need to access the parent's props and functions from the children.
The point is that you should never use React internals and undocumented APIs.
First of all, they are likely to change (breaking your code) and, most importantly, there are many other approaches which are cleaner.
Passing props to children
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
const Child = ({ fn }) => <button onClick={fn}>Click me!</button>
Working example
Using context (if there's no direct parent/child relation)
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
getChildContext() {
return {
fn: this.fn,
}
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
Parent.childContextTypes = {
fn: React.PropTypes.func,
}
const Child = (props, { fn }) => <button onClick={fn}>Click me!</button>
Child.contextTypes = {
fn: React.PropTypes.func,
}
Working example
Update for React 0.13 and newer
Component._owner was deprecated in React 0.13, and _currentElement no longer exists as a key in this._reactInternalInstance. Therefore, using the solution below throws Uncaught TypeError: Cannot read property '_owner' of undefined.
The alternative is, as of React 16, this._reactInternalFiber._debugOwner.stateNode.
You've already recognized that this is not a good thing to do almost always, but I'm repeating it here for people that don't read the question very well: this is generally an improper way to get things done in React.
There's nothing in the public API that will allow you to get what you want. You may be able to get to this using the React internals, but because it's a private API it's liable to break at any time.
I repeat: you should almost certainly not use this in any sort of production code.
That said, you can get the internal instance of the current component using this. _reactInternalInstance. In there, you can get access to the element via the _currentElement property, and then the owner instance via _owner._instance.
Here's an example:
var Parent = React.createClass({
render() {
return <Child v="test" />;
},
doAThing() {
console.log("I'm the parent, doing a thing.", this.props.testing);
}
});
var Child = React.createClass({
render() {
return <button onClick={this.onClick}>{this.props.v}</button>
},
onClick() {
var parent = this._reactInternalInstance._currentElement._owner._instance;
console.log("parent:", parent);
parent.doAThing();
}
});
ReactDOM.render(<Parent testing={true} />, container);
And here's a working JSFiddle example: http://jsfiddle.net/BinaryMuse/j8uaq85e/
Tested with React 16
I was playing around with something similar using context, tho to anyone reading this, for most usual cases, accessing the parent is not advised!
I created a holder that when used, would always have a reference to the first holder up the display list, so its 'parent' if you will. Looked something like this:
const ParentContext = React.createContext(null);
// function to apply to your react component class
export default function createParentTracker(componentClass){
class Holder extends React.PureComponent {
refToInstance
render(){
return(
<ParentContext.Consumer>
{parent => {
console.log('I am:', this, ' my parent is:',parent ? parent.name : 'null');
return(
<ParentContext.Provider value={this}>
<componentClass ref={inst=>refToInstance=inst} parent={parent} {...this.props} />
</ParentContext.Provider>
)}
}
</ ParentContext.Consumer>
)
}
}
// return wrapped component to be exported in place of yours
return Holder;
}
Then to use it you would pass your react component to the method when you export it like so:
class MyComponent extends React.Component {
_doSomethingWithParent(){
console.log(this.props.parent); // holder
console.log(this.props.parent.refToInstance); // component
}
}
// export wrapped component instead of your own
export default createParentTracker(MyComponent);
This way any component exporting the function will get its parent's holder passed in as a prop (or null if nothing is further up the hierarchy). From there you can grab the refToInstance. It will be undefined until everything is mounted though.

Resources